Які різниці між C ++ 03 та C ++ 11 можна виявити під час виконання?


116

Можна записати функцію, яка при компіляції з компілятором C поверне 0, а при компіляції з компілятором C ++ поверне 1 (тривіальне сулюювання з #ifdef __cplusplusне цікаво).

Наприклад:

int isCPP()
{
    return sizeof(char) == sizeof 'c';
}

Звичайно, вищезазначене буде працювати лише в тому випадку, якщо sizeof (char)воно не те самеsizeof (int)

Інше, більш портативне рішення - це щось подібне:

int isCPP()
{
    typedef int T;
    {
       struct T 
       {
           int a[2];
       };
       return sizeof(T) == sizeof(struct T);
    }
}

Я не впевнений, чи приклади на 100% вірні, але ви розумієте. Я вважаю, що є й інші способи написання тієї самої функції.

Які різниці між C ++ 03 та C ++ 11 можна виявити під час виконання? Іншими словами, чи можна написати подібну функцію, яка б повернула булеве значення, вказуючи, чи вона складена відповідним компілятором C ++ 03 або компілятором C ++ 11?

bool isCpp11()
{ 
    //???
} 

10
І який сенс цієї вправи? По-перше, у вас є макрос, по-друге, пройде кілька років, перш ніж компілятори почнуть реалізовувати всі функції C ++ 0x, тим часом це буде суміш. Тож єдиним розумним тестом є компілятор макросу версії.
Джин Бушуєв,

4
Це відповідає законопроекту не справжнього питання, але здається, що занадто цікаво дотримуватися правил!
Девід Геффернан

4
@Gene та ін: Чи ти спростовуєш усі цікаві питання, але ти не бачиш прагматичного "пункту"?
Армен Цирунян

2
"Ми очікуємо, що відповіді, як правило, стосуються фактів, посилань чи конкретної експертизи". Я думаю, що це питання відповідає цим очікуванням, проголосуйте за повторне відкриття.
Карел Петранек

6
@sixlettervariables: хоча це, безумовно, відкрито для аргументу, що фразування може бути кращим, мені здається, що основне поняття питання (які відмінності, якщо такі є, між C ++ 03 та C ++ 0x, можна виявити при запуску- час?) цілком законно. Зважаючи на те, що код має компілюватися та виконуватись в обох, він також може бути викладений як про проривні зміни в C ++ 0x. Мені це здається також цілком законним питанням.
Джеррі Труну

Відповіді:


108

Основна мова

Доступ до обчислювача за допомогою :::

template<int> struct int_ { };

template<typename T> bool isCpp0xImpl(int_<T::X>*) { return true; }
template<typename T> bool isCpp0xImpl(...) { return false; }

enum A { X };
bool isCpp0x() {
  return isCpp0xImpl<A>(0);
}

Ви також можете зловживати новими ключовими словами

struct a { };
struct b { a a1, a2; };

struct c : a {
  static b constexpr (a());
};

bool isCpp0x() {
  return (sizeof c::a()) == sizeof(b);
}

Також факт, що рядкові літерали більше не перетворюються на char*

bool isCpp0xImpl(...) { return true; }
bool isCpp0xImpl(char*) { return false; }

bool isCpp0x() { return isCpp0xImpl(""); }

Я не знаю, наскільки ймовірно, що ви працюєте над реальною реалізацією. Той, що експлуатуєauto

struct x { x(int z = 0):z(z) { } int z; } y(1);

bool isCpp0x() {
  auto x(y);
  return (y.z == 1);
}

Далі базується на тому, що operator int&&це функція перетворення int&&в C ++ 0x, а перетворення до якої intслід логічно-і в C ++ 03

struct Y { bool x1, x2; };

struct A {
  operator int();
  template<typename T> operator T();
  bool operator+();
} a;

Y operator+(bool, A);

bool isCpp0x() {
  return sizeof(&A::operator int&& +a) == sizeof(Y);
}

Цей тестовий випадок не працює для C ++ 0x в GCC (схожий на помилку) і не працює в режимі C ++ 03 для clang. Поданий клац-піар .

Модифікована обробка вводяться імен класів шаблонів в C ++ 11:

template<typename T>
bool g(long) { return false; }

template<template<typename> class>
bool g(int) { return true; }

template<typename T>
struct A {
  static bool doIt() {
    return g<A>(0);
  }
};

bool isCpp0x() {
  return A<void>::doIt();
}

Пара "виявити, чи це C ++ 03 або C ++ 0x" може бути використана для демонстрації порушень змін. Далі йде виправлена ​​тестова скринька, яка спочатку використовувалася для демонстрації такої зміни, але тепер використовується для тестування на C ++ 0x або C ++ 03.

struct X { };
struct Y { X x1, x2; };

struct A { static X B(int); };
typedef A B;

struct C : A {
  using ::B::B; // (inheriting constructor in c++0x)
  static Y B(...);
};

bool isCpp0x() { return (sizeof C::B(0)) == sizeof(Y); }

Стандартна бібліотека

Виявлення відсутності operator void*в C ++ 0x 'std::basic_ios

struct E { E(std::ostream &) { } };

template<typename T>
bool isCpp0xImpl(E, T) { return true; }
bool isCpp0xImpl(void*, int) { return false; }

bool isCpp0x() {
  return isCpp0xImpl(std::cout, 0);
}

1
Приємно. Я можу підтвердити, що це рішення працює тут з g ++ (GCC) 4.6.0, як і без -std = c ++ 0x.
Олександр

2
Це повертається trueдо MSVC 2005 і далі, і помилка компіляції в MSVC 2003.
Ентоні Вільямс,

1
О, дорогі, вони зламали зворотну сумісність!
avakar

14
@Johannes: Це найсмішніше, що ти провів за тижні, чи не так? ; -]
ildjarn

4
Мені здається, що це дуже цікаво, але я вважаю, що найрозумнішим є (...)vs (char*)call. Мені це дуже подобається!
corsiKa

44

Я отримав натхнення з „ Які переломні зміни вводяться в C ++ 11? :

#define u8 "abc"

bool isCpp0x() {
   const std::string s = u8"def"; // Previously "abcdef", now "def"
   return s == "def";
}

Це засновано на нових літеральних рядках, які мають перевагу над розширенням макросу.


1
+1: Дійсно, дуже цікаво, але технічно порушується вимога не використовувати препроцесор. Але обмеження не мало наміру відхиляти такі приємні відповіді :)
Армен Цірунян

1
Добре, якщо ви дотримуєтесь функції за допомогою a #undef u8 тодішнього використання препроцесора, можна спостерігати лише у тому випадку, якщо у вашій програмі є раніше визначений макрос u8(boooo). Якщо це викликає серйозне занепокоєння, це все ще можна вирішити, використовуючи специфічні для впровадження push / pop макропрагми / виклики (я вважаю, що більшість реалізацій мають такі).
James McNellis

3
Досить розумним аргументом є те, що в системі C ++ 03 хтось може #define u8 надати змодельовані можливості C ++ 0x. Все-таки мені дуже подобається відповідь.
Крістофер Сміт

1
ви можете просто перемістити цю функцію isCpp0x до окремої одиниці перекладу, так що ця макрореєстрація не вплине на інший код.
unkulunkulu

1
Я думаю, що різниця між використанням препроцесора покладатися на компілятор, який встановлює якесь значення макросу, і використанням препроцесора для виявлення фактичних особливостей мови. Ось чому я не думаю, що ця відповідь обманює.
Гонки легкості на орбіті

33

Як щодо перевірки за допомогою нових правил >>закриття шаблонів:

#include <iostream>

const unsigned reallyIsCpp0x=1;
const unsigned isNotCpp0x=0;

template<unsigned>
struct isCpp0xImpl2
{
    typedef unsigned isNotCpp0x;
};

template<typename>
struct isCpp0xImpl
{
    static unsigned const reallyIsCpp0x=0x8000;
    static unsigned const isNotCpp0x=0;
};

bool isCpp0x() {
    unsigned const dummy=0x8000;
    return isCpp0xImpl<isCpp0xImpl2<dummy>>::reallyIsCpp0x > ::isNotCpp0x>::isNotCpp0x;
}

int main()
{
    std::cout<<isCpp0x()<<std::endl;
}

Крім того, швидка перевірка std::move :

struct any
{
    template<typename T>
    any(T const&)
    {}
};

int move(any)
{
    return 42;
}

bool is_int(int const&)
{
    return true;
}

bool is_int(any)
{
    return false;
}


bool isCpp0x() {
    std::vector<int> v;
    return !is_int(move(v));
}

6
+1 Класна ідея дійсно :) Однак на практиці це порушиться з Visual C ++ 2005/2088, які не підтримують C ++ 0x, але дозволяють >> використовувати в шаблонах способом C ++ 0x.
Карел Петранек

4
Ооо; Мені подобається зловживання ADL! Однак, чи не могла відповідна реалізація C ++ 03 не мати функцію std::move?
James McNellis

1
@FredOverflow: Я б не переймався. Інтерфейс смокче!
Гонки легкості по орбіті

16

На відміну від попереднього C ++, C ++ 0x дозволяє створювати еталонні типи з типів посилань, якщо цей базовий тип посилання вводиться, наприклад, через параметр шаблону:

template <class T> bool func(T&) {return true; } 
template <class T> bool func(...){return false;} 

bool isCpp0x() 
{
    int v = 1;
    return func<int&>(v); 
}

На жаль, ідеальне переадресація, на жаль, є ціною порушення сумісності назад.

Інший тест може базуватися на дозволених локальних типах як аргументах шаблонів:

template <class T> bool cpp0X(T)  {return true;} //cannot be called with local types in C++03
                   bool cpp0X(...){return false;}

bool isCpp0x() 
{
   struct local {} var;
   return cpp0X(var);
}

Можливо, я мав би перетворити це на клас ознак;)
uwedolinsky

1
+1 Хороша ідея, я не впевнений, чи isC++0xдійсний ідентифікатор C ++;)
Karel Petranek

1
Що є хорошим посиланням для посилання з висновку посилань?
Керрек СБ

@ kerrek-sb: Проект розповідає про це в 8.3.2.6 (Посилання)
uwedolinsky

15

Це не зовсім правильний приклад, але це цікавий приклад, який дозволяє розрізнити C проти C ++ 0x (хоча він недійсний C ++ 03):

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}

10
Технічно це спирається на sizeof(int) != 1істинність. У системі 0x з виключно великим chars результати можуть бути однаковими. І все-таки акуратний трюк.
Dennis Zickefoose

@Dennis - charце завжди один байт, хоча
вузол

4
@Node: один байт - це не завжди 8 біт.
Олександр К.

2
@Node sizeof(char)завжди буде 1, за визначенням. Але CHAR_BIT(визначено обмеження.h) дозволено бути більше 8. В результаті обидва charта intможуть мати 32 біти, у цьому випадку sizeof(int) == 1CHAR_BIT == 32).
Sjoerd

12

З цього питання :

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;

Мені було цікаво, як це може працювати; спробувавши його, зараз зрозуміло: маленька помилка. він працює, якщо змінити останній рядок на:bool is_cpp0x = !test[0].flag;
awx

1
правдоподібно: C ++ 0x конструкцій за замовчуванням, Tтоді як C ++ 03 копіює конструкції зT()
awx

9

Хоча це не так стисло ... У поточному C ++ назва шаблону класу сама інтерпретується як ім'я типу (а не ім'я шаблону) в області шаблону цього класу. З іншого боку, назва шаблону класу може використовуватися як назва шаблону в C ++ 0x (N3290 14.6.1 / 1).

template< template< class > class > char f( int );
template< class > char (&f(...))[2];

template< class > class A {
  char i[ sizeof f< A >(0) ];
};

bool isCpp0x() {
  return sizeof( A<int> ) == 1;
}

9
#include <utility>

template<typename T> void test(T t) { t.first = false; }

bool isCpp0x()
{
   bool b = true;
   test( std::make_pair<bool&>(b, 0) );
   return b;
}

NB технічно це тестує стандартну бібліотеку, а не компілятор, і хоча вона дійсна C ++ 03 і дійсна C ++ 0x, це не вірно C ++ 98, тому з деякими налаштуваннями можна було б визначити C ++ 98 / C ++ 03 / C ++ 0x stdlib
Джонатан Уейклі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.