Так, існує ціла кількість змін, які призведуть до того, що один і той же код призведе до різної поведінки між 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 року . Говард Хіннант прокоментував значення цієї зміни в потоці на те, чи є ліва зміщення (<<) негативним цілим невизначеним поведінкою в C ++ 11? .
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 специфікації виключень для ваших деструкторів неявно генеруються
Оскільки в 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 звуження перетворення в сукупних ініціалізаторах неправильно сформовано, і схоже, що gcc
це дозволяє як у C ++ 11, так і в C ++ 03, хоча це дає попередження за замовчуванням у C ++ 11:
int x[] = { 2.0 };
Це розглядається в 11 стандартний розділ проекту C ++ 8.5.4
Список ініціалізації пункту 3 :
Ініціалізація списку об'єкта або посилання типу T визначається наступним чином:
і містить таку кульку ( акцентна міна ):
В іншому випадку, якщо T - клас класу, конструктори розглядаються. Застосовувані конструктори перераховуються, а найкращий вибирається за допомогою роздільної здатності перевантаження (13.3, 13.3.1.7). Якщо для перетворення будь-якого з аргументів потрібно звуження перетворення (див. Нижче), програма неправильно формується
Цей та багато інших прикладів висвітлено у проекті стандартних розділів annex C.2
C ++ та 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
Вкажіть округлення для результатів цілого / і% [...] коду 2003 року, який використовує ціле ділення, округляє результат до 0 або до негативної нескінченності, тоді як цей Міжнародний стандарт завжди округляє результат до 0.
Складність функцій-членів розміру () тепер постійна [...] Деякі реалізації контейнерів, які відповідають C ++ 2003, можуть не відповідати заданим вимогам розміру () цього Міжнародного стандарту. Настроювання контейнерів, таких як std :: list, суворішим вимогам може вимагати несумісних змін.
Зміна базового класу std :: ios_base :: fail [...] std :: ios_base :: відмова більше не походить безпосередньо від std :: винятку, але тепер походить від std :: system_error, який у свою чергу походить від std :: runtime_error. Дійсний код C ++ 2003, який передбачає, що std :: ios_base :: похибка походить безпосередньо від std :: виключення може виконуватися по-різному в цьому Міжнародному стандарті.
auto
може призвести до подібної ситуації