Может ли код C++ быть действительным как в C++03, так и в C++11, но делать разные вещи?
возможно ли, чтобы код C++ соответствовал обоим C++03 стандартные и C++11 стандартные, но делать разные вещи в зависимости от того, под какой стандарт он составляется?
7 ответов:
ответ Определенно да. С положительной стороны есть:
- код, который ранее неявно копировал объекты, теперь неявно перемещает их, когда это возможно.
что касается отрицательной стороны, то в добавлении С к стандарту приводится несколько примеров. Несмотря на то, что существует гораздо больше отрицательных, чем положительных, каждый из них гораздо менее вероятен.
строка литералы
#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<int N> 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. Это, скорее всего, сломает существующий код, хотя: (from 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<fun<9> >(1) >> 2 in both standards unsigned int A = fon< fun< 9 > >(1) >>(2); // fon<fun<4> >(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<double> list; // ... size_t s = list.size(); // Might be an O(n) operation in C++03
std::ios_base::failure
не является прямым производным отstd::exception
большев то время как прямой базовый класс является новым,
std::runtime_error
нет. Таким образом:try { std::cin >> variable; // exceptions enabled, and error here } catch(std::runtime_error &) { std::cerr << "C++11\n"; } catch(std::ios_base::failure &) { std::cerr << "Pre-C++11\n"; }
Я вам!--6-->в этой статье и в последующем, который имеет хороший пример того, как
>>
можно изменить значение С C++03 на C++11, все еще компилируя в обоих.bool const one = true; int const two = 2; int const three = 3; template<int> struct fun { typedef int two; }; template<class T> 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 один действительно придумал предупреждающая форма лязгнула, когда я проверил ее.
Да, есть ряд изменений, которые приведут к тому, что один и тот же код приведет к различному поведению между 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 включают
N3485
который я ссылаюсь ниже исправлено неопределенное поведение сдвига 1 бит в знаковый бит или мимо него. Это также рассматривается в отчет о дефекте 1457. Говард Хиннант прокомментировал значение этого изменения в нить на является ли сдвиг влево ( .5. функции constexpr можно рассматривать как выражения константы времени компиляции в C++11
в C++11 введен constexpr функции:
спецификатор constexpr объявляет, что можно вычислить значение функции или переменной во время компиляции. Такие переменные и функции могут быть используется там, где разрешены только выражения констант времени компиляции.
в то время как C++03 не имеет constexpr функция мы не должны явно использовать constexpr ключевое слово, так как стандартная библиотека предоставляет множество функций в C++11 как constexpr. Например std:: numeric_limits:: min. Что может привести к различному поведению, например:
#include <limits> int main() { int x[std::numeric_limits<unsigned int>::min()+2] ; }
используя
clang
в C++03 это причинаx
быть массивом переменной длины, который расширение и выдаст следующее предупреждение:warning: variable length arrays are a C99 feature [-Wvla-extension] int x[std::numeric_limits<unsigned int>::min()+2] ; ^
в то время как в C++11
std::numeric_limits<unsigned int>::min()+2
является выражением константы времени компиляции и не требует расширения VLA.6. В C++11 спецификации исключений noexcept неявно генерируются для ваших деструкторов
так как в C++11 определяемый пользователем деструктор имеет неявное
noexcept(true)
спецификация как объяснил в как noexcept деструкторы это означает, что следующую программу:#include <iostream> #include <stdexcept> 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 не принимает классы сравнения, объявленные в функции. Поэтому следующий код не должен работать в C++03:
#include <iostream> #include <vector> #include <algorithm> class Comparators { public: bool operator()(int first, int second) { return first < second; } }; int main() { class ComparatorsInner : public Comparators{}; std::vector<int> compares ; compares.push_back(20) ; compares.push_back(10) ; compares.push_back(30) ; ComparatorsInner comparatorInner; std::sort(compares.begin(), compares.end(), comparatorInner); std::vector<int>::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 <iostream> template<int I> struct X { static int const c = 2; }; template<> struct X<0> { typedef int c; }; template<typename T> struct Y { static int const c = 3; }; static int const c = 4; int main() { std::cout << (Y<X<1> >::c >::c>::c) << '\n'; std::cout << (Y<X< 1>>::c >::c>::c) << '\n'; }
и результат в C++03:
0 3
и в C++11:
0 0
9. C++11 изменяет некоторые конструкторы std::vector
слегка измененный код от ответ показывает, что с помощью следующего конструктора из std:: vector:
std::vector<T> test(1);
дает различные результаты в C++03 и C++11:
#include <iostream> #include <vector> struct T { bool flag; T() : flag(false) {} T(const T&) : flag(true) {} }; int main() { std::vector<T> test(1); bool is_cpp11 = !test[0].flag; std::cout << is_cpp11 << std::endl ; }
10. Сужение конверсий в агрегатных инициализаторах
в C++11 сужающее преобразование в инициализаторах aggregate плохо сформировано и выглядит как
gcc
позволяет это как в C++11, так и в C++03, хотя по умолчанию он предоставляет предупреждение в C++11:int x[] = { 2.0 };
это описано в проекте стандарта C++11 раздел
8.5.4
список-инициализации абзац 3:List-инициализация объекта или ссылки типа T определяется следующим образом:
и содержит следующую пулю (выделено мной):
в противном случае, если T-тип класса, рассматриваются конструкторы. Соответствующие конструкторы перечисляются, и лучший из них выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). если сужающее преобразование (см. ниже), необходимые для преобразования любого из аргументов программа плохо сформирована
этот и многие другие экземпляры рассматриваются в проект стандарта C++ раздел
annex C.2
C++ и ISO C++ 2003. Она также включает в себя:
новые виды строковых литералов [...] В частности, макросы с именами 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
укажите округление для результатов integer / и % [...] 2003 код, который использует целочисленное деление округляет результат до 0 или к отрицательной бесконечности, тогда как это Международный стандарт всегда округляет результат до 0.
сложность функций-членов size () теперь постоянна [...] Некоторые реализации контейнеров, соответствующие C++ 2003, могут не соответствовать требованиям указанного размера () в настоящем международном стандарте. Настройка контейнеров, таких как std::list, на более строгие требования может потребовать несовместимых изменений.
изменить базовый класс std::ios_base:: failure [...] std:: ios_base:: failure больше не является производным непосредственно от std:: exception, но теперь является производным от std:: system_error, который в свою очередь является производным от std:: runtime_error. Допустимый код C++ 2003, который предполагает, что std::ios_base::failure является производным непосредственно от std::exception, может выполняться по-разному в этом международном стандарте.
одно потенциально опасное обратно несовместимое изменение находится в конструкторах контейнеров последовательности, таких как
std::vector
, в частности, в перегрузке, указывающей начальный размер. Где в C++03 они скопировали элемент, построенный по умолчанию, в C++11 они по умолчанию строят каждый из них.рассмотрим этот пример (используя
boost::shared_ptr
так что это действительно C++03):#include <deque> #include <iostream> #include "boost/shared_ptr.hpp" struct Widget { boost::shared_ptr<int> p; Widget() : p(new int(42)) {} }; int main() { std::deque<Widget> d(10); for (size_t i = 0; i < d.size(); ++i) std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n'; }
причина в том, что 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);
второй работает как и раньше, создавая
size
копииprototype
элемент. Однако первый (который теперь обрабатывает вызовы только с указанным аргументом размера) по умолчанию-создает каждый элемент по отдельности.Я предполагаю, что причина этого изменения заключается в том, что перегрузка C++03 не будет использоваться с типом элемента только для перемещения. Но это тем не менее переломное изменение, и при этом редко документируется.
результат неудачного чтения из
std::istream
изменилось. CppReference подводит итог красиво:если извлечение не удается (например, если была введена буква, где ожидается цифра),
value
остается неизменным иfailbit
- это набор. (до C++11)если извлечение не удается, ноль записывается в
value
иfailbit
- это набор. Если извлечение приводит к значению слишком большому или слишком малому, чтобы вписаться вvalue
,std::numeric_limits<T>::max()
илиstd::numeric_limits<T>::min()
написаноfailbit
флаг установлен. (начиная с C++11)это в первую очередь проблема, если вы привыкли к новой семантике, а затем должны писать с помощью C++03. Следующее не является особенно хорошей практикой, но четко определено в C++11:
int x, y; std::cin >> x >> y; std::cout << x + y;
однако в C++03 приведенный выше код использует неинициализированную переменную и, таким образом, имеет неопределенное поведение.
этой теме какие различия, если таковые имеются, между C++03 и C++0x могут быть обнаружены во время выполнения имеет примеры (скопированные из этого потока) для определения языковых различий, например, используя C++11 reference collapsing:
template <class T> bool f(T&) {return true; } template <class T> bool f(...){return false;} bool isCpp11() { int v = 1; return f<int&>(v); }
и c++11, разрешающие локальные типы в качестве параметров шаблона:
template <class T> 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 <iostream> template<class T> struct has { typedef char yes; typedef yes (&no)[2]; template<int> struct foo; template<class U> static yes test(foo<U::bar>*); template<class U> static no test(...); static bool const value = sizeof(test<T>(0)) == sizeof(yes); }; enum foo { bar }; int main() { std::cout << (has<foo>::value ? "yes" : "no") << std::endl; }
принты:
Using c++03: no Using c++11: yes