Да, есть ряд изменений, которые приведут к тому, что один и тот же код приведет к разному поведению между 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 };
Это описано в проекте стандартного раздела C ++ 11 8.5.4
List-initialization, параграф 3 :
Инициализация списка объекта или ссылки типа 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
Укажите округление результатов для целочисленного / и кода% [...] 2003, в котором используется целочисленное деление, округляет результат до 0 или до отрицательной бесконечности, тогда как этот международный стандарт всегда округляет результат до 0.
Сложность функций-членов size () теперь постоянна [...] Некоторые реализации контейнеров, соответствующие C ++ 2003, могут не соответствовать указанным требованиям size () в этом международном стандарте. Приспособление контейнеров, таких как std :: list, к более строгим требованиям может потребовать несовместимых изменений.
Изменить базовый класс std :: ios_base :: fail [...] std :: ios_base :: fail больше не выводится напрямую из std :: exception, а теперь выводится из std :: system_error, который, в свою очередь, выводится из станд :: runtime_error. Допустимый код C ++ 2003, который предполагает, что std :: ios_base :: fail является производным непосредственно от std :: exception, может выполняться по-другому в этом международном стандарте.
auto
может привести к такой ситуации