Может ли код C ++ быть действительным как в C ++ 03, так и в C ++ 11, но делать разные вещи?

Возможно ли, чтобы код C ++ соответствовал стандарту C ++ 03 и стандарту C ++ 11 , но выполнял различные действия в зависимости от того, какой стандарт он компилируется?

Ответ – это определенное да. С положительной стороны:

  • Код, который ранее неявно скопировал объекты, теперь неявно перемещает их, когда это возможно.

С отрицательной стороны несколько примеров приведены в приложении C стандарта. Несмотря на то, что существует много отрицательных, чем положительных, каждый из них гораздо менее вероятен.

Строковые литералы

 #define u8 "abc" const char* s = u8"def"; // Previously "abcdef", now "def" 

а также

 #define _x "there" "hello "_x // Previously "hello there", now a user defined string literal 

Тип преобразования 0

В C ++ 11 только литералы являются целыми константами нулевого указателя:

 void f(void *); // #1 void f(...); // #2 template void g() { f(0*N); // Calls #2; used to call #1 } 

Закругленные результаты после целочисленного деления и по модулю

В C ++ 03 компилятору разрешалось либо округлять к 0, либо к отрицательной бесконечности. В C ++ 11 обязательно округлять до 0

 int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0 

Пробелы между закрытыми скобками вложенных шаблонов >> vs>>

Внутри специализации или инстанцирования >> вместо этого можно интерпретировать как сдвиг вправо в C ++ 03. Это, скорее всего, приведет к нарушению существующего кода: (от http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )

 template< unsigned len > unsigned int fun(unsigned int x); typedef unsigned int (*fun_t)(unsigned int); template< fun_t f > unsigned int fon(unsigned int x); void total(void) { // fon >(1) >> 2 in both standards unsigned int A = fon< fun< 9 > >(1) >>(2); // fon >(2) in C++03 // Compile time error in C++11 unsigned int B = fon< fun< 9 >>(1) > >(2); } 

new оператор теперь может использовать другие исключения, кроме std::bad_alloc

 struct foo { void *operator new(size_t x){ throw std::exception(); } } try { foo *f = new foo(); catch (std::bad_alloc &) { // c++03 code } catch (std::exception &) { // c++11 code } 

Объявленные пользователем деструкторы имеют неявный пример спецификации исключений из того, какие нарушения были внесены в C ++ 11?

 struct A { ~A() { throw "foo"; } // Calls std::terminate in C++11 }; //... try { A a; } catch(...) { // C++03 will catch the exception } 

size() контейнеров теперь требуется для работы в O (1)

 std::list list; // ... size_t s = list.size(); // Might be an O(n) operation in C++03 

std::ios_base::failure больше не происходит из std::exception

Код, который ожидает, что он будет выводиться непосредственно из std::exception может вести себя по-разному.

Я указываю вам эту статью и последующую , которая имеет хороший пример того, как >> может изменить значение с C ++ 03 на C ++ 11, все еще компилируя в обоих.

 bool const one = true; int const two = 2; int const three = 3; template struct fun { typedef int two; }; template struct fon { static int const three = ::three; static bool const one = ::one; }; int main(void) { fon< fun< 1 >>::three >::two >::one; // valid for both } 

Ключевой частью является строка в main , которая является выражением.

В C ++ 03:

 1 >> ::three = 0 => fon< fun< 0 >::two >::one; fun< 0 >::two = int => fon< int >::one fon< int >::one = true => true 

В C ++ 11

 fun< 1 > is a type argument to fon fon< fun<1> >::three = 3 => 3 > ::two > ::one ::two is 2 and ::one is 1 => 3 > 2 > 1 => (3 > 2) > 1 => true > 1 => 1 > 1 => false 

Поздравляем, два разных результата для одного и того же выражения. Конечно, C ++ 03 придумал предупреждающую форму Clang, когда я ее протестировал.

Да, есть ряд изменений, которые приведут к тому, что один и тот же код приведет к другому поведению между C ++ 03 и C ++ 11. Различия в правилах последовательности приводят к некоторым интересным изменениям, в том числе к некоему определенному ранее неопределенному поведению.

1. множественные мутации одной и той же переменной в списке инициализаторов

Один очень интересный краевой случай имел бы несколько мутаций одной и той же переменной в списке инициализаторов, например:

 int main() { int count = 0 ; int arrInt[2] = { count++, count++ } ; return 0 ; } 

В C ++ 03 и C ++ 11 это хорошо определено, но порядок оценки в C ++ 03 не указан, но в C ++ 11 они оцениваются в том порядке, в котором они появляются . Поэтому, если мы скомпилируем с помощью clang в режиме C ++ 03, оно предоставит следующее предупреждение ( см. Его в прямом эфире ):

 warning: multiple unsequenced modifications to 'count' [-Wunsequenced] int arrInt[2] = { count++, count++ } ; ^ ~~ 

но не дает предупреждения в C ++ 11 ( см. его в прямом эфире ).

2. Новые правила последовательности делают i = ++ i + 1; хорошо определен в C ++ 11

Новые правила последовательности, принятые после C ++ 03, означают, что:

 int i = 0 ; i = ++ i + 1; 

больше не является неопределенным поведением в C ++ 11, это описано в отчете о дефектах 637. Правила и примеры секвенирования не согласуются

3. Новые правила упорядочения также делают ++++ i; хорошо определен в C ++ 11

Новые правила последовательности, принятые после C ++ 03, означают, что:

 int i = 0 ; ++++i ; 

больше не является неопределенным поведением в C ++ 11.

4. Чуть более чувствительные подписи с левым сдвигом

Более поздние черновики C ++ 11 include N3485 которые я N3485 ниже, фиксировали неопределенное поведение смещения 1 бит в или мимо знакового бита . Это также рассматривается в отчете 1457 о дефектах . Ховард Хиннант прокомментировал значимость этого изменения в streamе. Есть ли сдвиг слева (<<) отрицательное целое неопределенное поведение в C ++ 11? ,

5. Функции constexpr можно рассматривать как выражения постоянной времени компиляции в C ++ 11

В C ++ 11 были введены функции constexpr, которые:

Спецификатор constexpr объявляет, что во время компиляции можно оценить значение функции или переменной. Такие переменные и функции могут использоваться тогда, когда допускаются только компиляции констант времени.

в то время как C ++ 03 не имеет функции constexpr, нам не нужно явно использовать ключевое слово constexpr, поскольку стандартная библиотека предоставляет множество функций в C ++ 11 как constexpr . Например std :: numeric_limits :: min . Это может привести к поведению, например:

 #include  int main() { int x[std::numeric_limits::min()+2] ; } 

Используя clang в C ++ 03, это приведет к тому, что x будет массивом переменной длины, который является расширением и будет генерировать следующее предупреждение:

 warning: variable length arrays are a C99 feature [-Wvla-extension] int x[std::numeric_limits::min()+2] ; ^ 

а в C ++ 11 std::numeric_limits::min()+2 – выражение постоянной времени компиляции и не требует расширения VLA.

6. В C ++ 11 noexcept спецификации исключения неявно генерируются для ваших деструкторов

Поскольку в C ++ 11 пользовательский деструктор имеет неявную спецификацию noexcept(true) как описано в noexcept destructors, это означает, что следующая программа:

 #include  #include  struct S { ~S() { throw std::runtime_error(""); } // bad, but acceptable }; int main() { try { S s; } catch (...) { std::cerr << "exception occurred"; } std::cout << "success"; } 

В C ++ 11 вызовет std::terminate но будет успешно запущен в C ++ 03.

7. В C ++ 03 аргументы шаблона не могут иметь внутренней связи

Это хорошо описано в разделе Почему std :: sort не принимает classы сравнения, объявленные внутри функции . Поэтому следующий код не должен работать в C ++ 03:

 #include  #include  #include  class Comparators { public: bool operator()(int first, int second) { return first < second; } }; int main() { class ComparatorsInner : public Comparators{}; std::vector compares ; compares.push_back(20) ; compares.push_back(10) ; compares.push_back(30) ; ComparatorsInner comparatorInner; std::sort(compares.begin(), compares.end(), comparatorInner); std::vector::iterator it; for(it = compares.begin(); it != compares.end(); ++it) { std::cout << (*it) << std::endl; } } 

но в настоящее время clang разрешает этот код в режиме C ++ 03 с предупреждением, если вы не используете -pedantic-errors , который выглядит нехорошо, см. его вживую .

8. >> больше не плохо формируется при закрытии нескольких шаблонов

Использование >> для закрытия нескольких шаблонов больше не является плохо сформированным, но может привести к коду с разными результатами в C ++ 03 и C + 11. Пример ниже берется из прямоугольных скобок и обратной совместимости :

 #include  template struct X { static int const c = 2; }; template<> struct X<0> { typedef int c; }; template struct Y { static int const c = 3; }; static int const c = 4; int main() { std::cout << (Y >::c >::c>::c) << '\n'; std::cout << (Y>::c >::c>::c) << '\n'; } 

и результат в C ++ 03:

 0 3 

и в C ++ 11:

 0 0 

9. C ++ 11 изменяет некоторые конструкторы std :: vector

Немного измененный код из этого ответа показывает, что использование следующего конструктора из std :: vector :

 std::vector test(1); 

дает разные результаты в C ++ 03 и C ++ 11:

 #include  #include  struct T { bool flag; T() : flag(false) {} T(const T&) : flag(true) {} }; int main() { std::vector test(1); bool is_cpp11 = !test[0].flag; std::cout << is_cpp11 << std::endl ; } 

10. Сужение конверсий в совокупных инициализаторах

В C ++ 11 сужение конверсии в агрегатных инициализаторах плохо сформировано, и похоже, что gcc допускает это как в C ++ 11, так и в C ++ 03, хотя в C ++ 11 он предоставляет предупреждение по умолчанию:

 int x[] = { 2.0 }; 

Это описано в стандартном разделе проекта C ++ 11 8.5.4 Список-инициализация пункта 3 :

Инициализация списка объекта или ссылки типа T определяется следующим образом:

и содержит следующую пулю ( акцент мой ):

В противном случае, если T - тип classа, рассматриваются конструкторы. Применяемые конструкторы перечисляются, а лучший выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). Если для преобразования любого из аргументов требуется сужение конверсии (см. Ниже), программа плохо сформирована

Этот и многие другие экземпляры рассматриваются в проекте C ++ стандартного раздела annex C.2 C ++ и ISO C ++ 2003 . Он также включает:

  • Новые типы строковых литералов [...] В частности, macros с именами R, u8, u8R, u, uR, U, UR или LR не будут расширены, если они смежны с строковым литералом, но будут интерпретироваться как часть строкового литерала , Например

     #define u8 "abc" const char *s = u8"def"; // Previously "abcdef", now "def" 
  • Пользовательская строковая поддержка строки [...] Раньше # 1 состоял бы из двух отдельных токенов предварительной обработки, и макрос _x был бы расширен. В этом международном стандарте № 1 состоит из одного токена предварительной обработки, поэтому макрос не расширяется.

     #define _x "there" "hello"_x // #1 
  • Укажите округление для результатов целочисленного / и% [...] кода 2003, использующего целочисленное деление, округляет результат к 0 или к отрицательной бесконечности, тогда как этот международный стандарт всегда округляет результат до 0.

  • Сложность функций-членов size () теперь постоянна [...] Некоторые реализации контейнеров, которые соответствуют C ++ 2003, могут не соответствовать указанным требованиям размера () в этом Международном стандарте. Настройка контейнеров, таких как std :: list, на более строгие требования, может потребовать несовместимых изменений.

  • Изменить базовый class std :: ios_base :: failure [...] std :: ios_base :: failure больше не выводится непосредственно из std :: exception, но теперь происходит из std :: system_error, который, в свою очередь, получен из станд :: runtime_error. Действительный код C ++ 2003, предполагающий, что std :: ios_base :: failure выводится непосредственно из std :: exception, может выполняться по-другому в этом Международном стандарте.

Одно потенциально опасное обратное-несовместимое изменение заключается в конструкторах контейнеров последовательностей, таких как std::vector , в частности, при перегрузке с указанием начального размера. Где в C ++ 03 они скопировали созданный по умолчанию элемент, в C ++ 11 они по умолчанию построили каждый из них.

Рассмотрим этот пример (используя boost::shared_ptr чтобы он был действительным C ++ 03):

 #include  #include  #include "boost/shared_ptr.hpp" struct Widget { boost::shared_ptr p; Widget() : p(new int(42)) {} }; int main() { std::deque d(10); for (size_t i = 0; i < d.size(); ++i) std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n'; } 

Пример C ++ 03 Live

Пример C ++ 11 Live

Причина в том, что C ++ 03 указала одну перегрузку для «указать размер и элемент прототипа» и «указать только размер», как это (аргументы распределителя опущены для краткости):

 container(size_type size, const value_type &prototype = value_type()); 

Это всегда будет копировать prototype в size контейнера. Когда вызывается только одним аргументом, он будет создавать копии size созданного по умолчанию элемента.

В C ++ 11 эта подпись конструктора была удалена и заменена этими двумя перегрузками:

 container(size_type size); container(size_type size, const value_type &prototype); 

Второй работает по-прежнему, создавая копии prototype элемента prototype . Однако первый (который теперь обрабатывает вызовы только с указанным аргументом размера) по умолчанию создает каждый элемент по отдельности.

Мое предположение по причине этого изменения заключается в том, что перегрузка C ++ 03 не будет использоваться с типом элемента только для перемещения. Но это все же разрушительное изменение, и редко это подтверждалось.

Результат неудачного чтения из std::istream изменился. CppReference резюмирует это красиво:

Если извлечение завершится неудачей (например, если введена буква, в которой ожидается цифра), value остается неизмененным и failbit . (до C ++ 11)

Если извлечение завершается неудачно, нуль записывается в value и failbit . Если извлечения приводит к слишком большому или слишком маленькому value , чтобы соответствовать value , std::numeric_limits::max() или std::numeric_limits::min() и failbit флаг failbit . (поскольку C ++ 11)

Это прежде всего проблема, если вы привыкли к новой семантике, а затем должны писать с помощью C ++ 03. Ниже приведена не очень хорошая практика, но четко определенная в C ++ 11:

 int x, y; std::cin >> x >> y; std::cout << x + y; 

Однако в C ++ 03 приведенный выше код использует неинициализированную переменную и, следовательно, имеет неопределенное поведение.

Этот stream. В каких различиях между C ++ 03 и C ++ 0x можно обнаружить во время выполнения, есть примеры (скопированные из этого streamа), чтобы определить языковые различия, например, используя C ++ 11 сведение коллапса:

 template  bool f(T&) {return true; } template  bool f(...){return false;} bool isCpp11() { int v = 1; return f(v); } 

и c ++ 11, позволяющие локальным типам в качестве параметров шаблона:

 template  bool cpp11(T) {return true;} //T cannot be a local type in C++03 bool cpp11(...){return false;} bool isCpp0x() { struct local {} var; //variable with local type return cpp11(var); } 

Вот еще один пример:

 #include  template struct has { typedef char yes; typedef yes (&no)[2]; template struct foo; template static yes test(foo*); template static no test(...); static bool const value = sizeof(test(0)) == sizeof(yes); }; enum foo { bar }; int main() { std::cout << (has::value ? "yes" : "no") << std::endl; } 

Печать:

 Using c++03: no Using c++11: yes 

Посмотреть результат на Coliru

  • Понимание примера преобразования lvalue-to-rvalue
  • Является ли указатель с правильным адресом и типом все еще всегда действительным указателем с C ++ 17?
  • Передает ли объект C ++ в свой собственный конструктор законным?
  • Повторяющиеся typedefs - недопустимы в C, но действительны в C ++?
  • Итак, почему i = ++ i + 1 четко определен в C ++ 11?
  • Является ли размер std :: array определенным стандартом
  • Является ли это совместимым расширением компилятора для обработки стандартных библиотечных функций, отличных от constexpr, как constexpr?
  • Каков тип нулевого литерала?
  • Вывод первого аргумента шаблона с другими параметрами шаблона по умолчанию
  • Может ли SFINAE обнаруживать нарушения частного доступа?
  • Что такое «callback hell» и как и почему RX решает его?
  • Interesting Posts

    Можно ли установить 32-битные программы в «Program Files» в 64-битных окнах вместо «Program Files (x86)?

    Почему вы не можете изменять данные, возвращаемые Mongoose Query (например: findById)

    System.out объявляется как статический окончательный и инициализируется нулем?

    Скрыть скрытые параметры с помощью response.sendRedirect ()

    Создать / переименовать файл / папку, начинающуюся с точки в Windows?

    XPath содержит (текст (), «некоторая строка») не работает при использовании с узлом с более чем одним текстовым подзоном

    Преобразование геокоординат от степени к десятичной

    Можно ли постоянно использовать 100% -ное использование ЦП?

    Как отключить / включить диалоговые отрицательные положительные кнопки?

    Что такое «пакет» в приложении для Android

    Реализация OR в запросе firestore – Firebase firestore

    Как мы отличаемся от требований, не требующих ответа, в разрешениях Runtime Android M?

    RuntimeException: невозможно создать экземпляр приложения

    Windows XP зависает при завершении работы в VMWare Fusion

    Как включить ведение журнала сбоев сборки (Fusion) в .NET.

    Давайте будем гением компьютера.