Какие различия, если таковые имеются, между 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 et al: Вы отрицаете все интересующие вопросы, но не видите прагматической "точки"?
Армен Цирунян
2
«Мы ожидаем, что ответы, как правило, будут включать факты, ссылки или конкретный опыт». Думаю, этот вопрос соответствует этим ожиданиям, голосование за повторное открытие.
Карел Петранек
6
@sixlettervariables: хотя, безусловно, можно утверждать, что формулировка может быть лучше, мне кажется, что основная идея вопроса (какие различия, если таковые имеются, между C ++ 03 и C ++ 0x могут быть обнаружены при запуске - время?) вполне законно. Учитывая, что код должен компилироваться и выполняться в обоих, это также можно было бы сформулировать как о критических изменениях в C ++ 0x. Мне кажется, что этот вопрос тоже вполне законный.
Jerry Coffin,

Ответы:

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. Был подан лязг PR .

Модифицирована обработка вводимых имен классов шаблонов в 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 ++ это ли это или 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);
}
Йоханнес Шауб - litb
источник
1
Ницца. Я могу подтвердить, что это решение работает здесь с g ++ (GCC) 4.6.0, как с -std = c ++ 0x, так и без него.
Александр
2
Это возвращается trueдля MSVC 2005 и далее, а также ошибка компиляции в MSVC 2003.
Энтони Уильямс,
1
Ой боже, они сломали обратную совместимость!
avakar
14
@Johannes: Это самое веселое, что у тебя было за недели, не так ли? ; -]
ildjarn
4
Я нахожу эти очень все интересно, но я думаю , что самое умное это (...)против (char*)вызовов. Мне это очень нравится!
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 (я считаю, что в большинстве реализаций они есть).
Джеймс МакНеллис
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?
Джеймс МакНеллис
1
@FredOverflow: Я бы не стал беспокоиться. UI отстой!
Гонки легкости на орбите
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
источник
Возможно, мне следовало превратить это в класс черт;)
uwedolinsky
1
+1 Хорошая идея, я только не уверен, isC++0xдействительный ли это идентификатор C ++;)
Карел Петранек
1
Какая хорошая ссылка на ссылку из вывода ссылок?
Kerrek SB
@ 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всегда один байт
Node
4
@ Узел: один байт не всегда равен 8 битам.
Alexandre C.
2
@Node sizeof(char)всегда будет 1 по определению. Но CHAR_BIT(определено в limits.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
Джонатан Уэйкли,