Можно ли для кода C ++ соответствовать как стандарту C ++ 03, так и стандарту C ++ 11 , но делать разные вещи в зависимости от того, по какому стандарту он компилируется?
c++
c++11
language-lawyer
c++03
Эрик Шёлунд
источник
источник
auto
может привести к такой ситуации>>
использование в шаблоне. Вы можете столкнуться с ситуацией, когда он может компилироваться для обоих стандартов. Еще один, который, я уверен, будет легко найти изменения в инициализации.auto
может вызвать это. Со старым значением дляauto
объявления требуется имя типа; с новым значением имя типа не допускается.Ответы:
Ответ однозначный да. С положительной стороны есть:
С отрицательной стороны, несколько примеров перечислены в приложении С стандарта. Хотя негативных и позитивных гораздо больше, вероятность возникновения каждого из них значительно ниже.
Строковые литералы
и
Тип преобразования 0
В C ++ 11 только литералы являются константами целочисленных нулевых указателей:
Округленные результаты после целочисленного деления и по модулю
В C ++ 03 компилятору было разрешено либо округлять в сторону 0, либо в сторону отрицательной бесконечности. В C ++ 11 обязательно округлить до 0
Пробелы между вложенными фигурными скобками шаблона >> vs>>
Внутри специализации или реализации
>>
мог бы быть интерпретирован как сдвиг вправо в C ++ 03. Это, скорее всего, нарушит существующий код: (от http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )Оператор
new
теперь может выдавать другие исключения, кромеstd::bad_alloc
У деструкторов, объявленных пользователем, есть пример спецификации неявных исключений из раздела Какие критические изменения введены в C ++ 11?
size()
контейнеров теперь требуется для запуска в O (1)std::ios_base::failure
не вытекает непосредственно изstd::exception
большеХотя прямой базовый класс является новым,
std::runtime_error
это не так. Таким образом:источник
noexecpt(true)
поэтомуthrow
деструктор теперь будет вызыватьstd::terminate
. Но я надеюсь, что любой, кто написал такой код, будет рад этому!catch (std::exception &)
все равно ловитstd::ios_base::failure
.operator new
является точным (теперь оно можетstd::bad_array_new_length
показывать), но ваш пример совсем не показывает этого. Код, который вы показываете, одинаков в AFAIK C ++ 03 и C ++ 11.Я укажу вам на эту статью и продолжение , в котором есть хороший пример того, как
>>
можно изменить значение с C ++ 03 на C ++ 11, в то же время компилируя в обоих.Ключевой частью является строка
main
, которая является выражением.В C ++ 03:
В С ++ 11
Поздравляю, два разных результата для одного и того же выражения. Конечно, C ++ 03 при тестировании выдал предупреждение Clang.
источник
typename
для::two
C ++ 03 версииtrue
илиfalse
для различных стандартов. Может быть, мы могли бы использовать его в качестве функционального теста </ joke>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), но все же хороший пример того, как неоднозначный::
оператор меняет значение (либо ссылаясь на глобальную область, либо разыменовывая тот, который стоит прямо перед ним)Да, есть ряд изменений, которые приведут к тому, что один и тот же код приведет к разному поведению между C ++ 03 и C ++ 11. Различия в правилах секвенирования приводят к некоторым интересным изменениям, в том числе к определенному ранее неопределенному поведению, которое становится четко определенным.
1. несколько мутаций одной и той же переменной в списке инициализатора
В одном очень интересном угловом случае было бы несколько мутаций одной и той же переменной в списке инициализатора, например:
И в C ++ 03, и в C ++ 11 это хорошо определено, но порядок оценки в C ++ 03 не указан, но в C ++ 11 они оцениваются в том порядке, в котором они появляются . Поэтому, если мы скомпилируем с использованием
clang
режима C ++ 03, он выдаст следующее предупреждение ( см. Вживую ):но не предоставляет предупреждение в C ++ 11 ( см. вживую ).
2. Новые правила секвенирования делают i = ++ i + 1; хорошо определены в C ++ 11
Новые правила последовательности, принятые после C ++ 03, означают, что:
больше не является неопределенным поведением в C ++ 11, это описано в отчете о дефектах 637. Правила последовательности и примеры не согласны
3. Новые правила последовательности также делают ++++ i; хорошо определены в C ++ 11
Новые правила последовательности, принятые после C ++ 03, означают, что:
больше не является неопределенным поведением в C ++ 11.
4. Чуть более заметные подписанные сдвиги влево
Более поздние версии C ++ 11,
N3485
которые я привел ниже, исправили неопределенное поведение сдвига 1 бита в или после знака бита . Это также отражено в отчете о дефектах 1457 . Говард Хиннант прокомментировал значение этого изменения в потоке. Является ли сдвиг влево (<<) отрицательным целочисленным неопределенным поведением в C ++ 11? ,5. Функции constexpr могут рассматриваться как константы времени компиляции в C ++ 11
C ++ 11 представил функции constexpr, которые:
в то время как C ++ 03 не имеет функции constexpr, нам не нужно явно использовать ключевое слово constexpr, поскольку стандартная библиотека предоставляет множество функций в C ++ 11 в качестве constexpr . Например, std :: numeric_limits :: min . Что может привести к другому поведению, например:
При использовании
clang
в C ++ 03 это будетx
массив переменной длины, который является расширением и выдаст следующее предупреждение:в то время как в C ++ 11
std::numeric_limits<unsigned int>::min()+2
является константным выражением времени компиляции и не требует расширения VLA.6. В C ++ 11 спецификации исключений неявно генерируются для ваших деструкторов
Поскольку в C ++ 11 пользовательский деструктор имеет неявную
noexcept(true)
спецификацию, как объяснено в noexcept деструкторов, это означает, что следующая программа:В C ++ 11 будет вызывать,
std::terminate
но будет успешно работать в C ++ 03.7. В C ++ 03 аргументы шаблона не могут иметь внутреннюю связь
Это хорошо описано в разделе Почему std :: sort не принимает классы сравнения, объявленные внутри функции . Поэтому следующий код не должен работать в C ++ 03:
но в настоящее время
clang
этот код разрешен в режиме C ++ 03 с предупреждением, если только вы не используете-pedantic-errors
флаг, что довольно странно, смотрите вживую .8. >> больше не плохо работает при закрытии нескольких шаблонов
Использование
>>
для закрытия нескольких шаблонов больше не является неправильным, но может привести к коду с разными результатами в C ++ 03 и C + 11. Пример ниже взят из прямоугольных скобок и обратной совместимости :и результат в C ++ 03:
и в C ++ 11:
9. C ++ 11 меняет некоторые конструкторы std :: vector
Слегка измененный код из этого ответа показывает, что используется следующий конструктор из std :: vector :
дает разные результаты в C ++ 03 и C ++ 11:
10. Сужающие преобразования в агрегатных инициализаторах
В C ++ 11 сужающее преобразование в агрегатных инициализаторах плохо сформировано, и, похоже,
gcc
позволяет это как в C ++ 11, так и в C ++ 03, хотя по умолчанию в C ++ 11 выдает предупреждение:Это описано в проекте стандартного раздела C ++ 11
8.5.4
List-initialization, параграф 3 :и содержит следующую пулю ( выделено мое ):
Этот и многие другие примеры описаны в разделе C ++ проекта стандарта
annex C.2
C ++ и ISO C ++ 2003 . Также включает в себя:Новые виды строковых литералов [...] В частности, макросы с именами R, u8, u8R, u, uR, U, UR или LR не будут расширены, если они соседствуют со строковым литералом, но будут интерпретироваться как часть строкового литерала. , Например
Определяемая пользователем поддержка строковых литералов [...] Ранее, # 1 состоял бы из двух отдельных токенов предварительной обработки, и макрос _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, может выполняться по-другому в этом международном стандарте.
источник
Одно потенциально опасное обратное несовместимое изменение заключается в конструкторах контейнеров последовательностей, таких как
std::vector
, в частности, в перегрузке, задающей начальный размер. Где в C ++ 03 они скопировали элемент, созданный по умолчанию, в C ++ 11 они создали каждый элемент по умолчанию.Рассмотрим этот пример (используя
boost::shared_ptr
так, чтобы он действовал C ++ 03):C ++ 03 Живой пример
C ++ 11 Живой пример
Причина в том, что C ++ 03 указал одну перегрузку как для «указать размер и элемент прототипа», так и «указать только размер», как это (аргументы распределителя для краткости опущены):
Это всегда будет копировать
prototype
в контейнерsize
раз. При вызове только с одним аргументом он будет создаватьsize
копии сконструированного по умолчанию элемента.В C ++ 11 эта сигнатура конструктора была удалена и заменена этими двумя перегрузками:
Второй работает как прежде, создавая
size
копииprototype
элемента. Однако первый (который теперь обрабатывает вызовы только с указанным аргументом размера) по умолчанию создает каждый элемент отдельно.Я полагаю, что причина этого изменения в том, что перегрузка C ++ 03 не может быть использована с типом элемента только для перемещения. Но, тем не менее, это серьезное изменение, которое редко документируется.
источник
deque
получено десять отдельных виджетов, а не десять виджетов, использующих один и тот же ресурс.Результат неудачного чтения из
std::istream
изменился. CppReference суммирует это красиво:Это в первую очередь проблема, если вы привыкли к новой семантике, а затем должны писать с использованием C ++ 03. Следующее не является особенно хорошей практикой, но хорошо определено в C ++ 11:
Однако в C ++ 03 приведенный выше код использует неинициализированную переменную и, следовательно, имеет неопределенное поведение.
источник
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. С C ++ 03 это будет правильно производить,x
когда неy
может быть прочитано.Этот поток Какие различия, если таковые имеются, между C ++ 03 и C ++ 0x, которые могут быть обнаружены во время выполнения, имеют примеры (скопированные из этого потока) для определения языковых различий, например, путем использования свертывания ссылок C ++ 11:
и c ++ 11, разрешающий локальные типы в качестве параметров шаблона:
источник
Вот еще один пример:
Печать:
Посмотреть результат на Coliru
источник