Какие изменения, внесенные в C++14, потенциально могут сломать программу, написанную на C++11?

Введение

Поскольку стандарт C++14 (также известный как C++1y) находится в состоянии, близком к окончательному, программисты должны задать себе вопрос об обратной совместимости и проблемах, связанных с такими проблемами. .


Вопрос

В ответах на этот вопрос указано, что в Стандарте есть Приложение, посвященное информации относительно изменения между ревизиями.

Было бы полезно, если бы эти потенциальные проблемы в ранее упомянутом Приложении можно было объяснить, возможно, с помощью каких-либо официальных документов, связанных с тем, что там упоминается.

  • Согласно стандарту. Какие изменения, внесенные в C++14, могут нарушить работу программы, написанной на C++11?

person Filip Roséen - refp    schedule 01.06.2014    source источник


Ответы (1)


Примечание. В этом посте я считаю, что "критическое изменение" относится к одному из двух или к обоим;
1. a change that will make legal C++11 ill-formed when compiled as C++14, and;
2 . изменение, которое изменит поведение во время выполнения при компиляции C++14 по сравнению с C++11.


C++11 и C++14, что говорит стандарт?

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

В этом посте этот раздел [diff.cpp11] использовался как основа для полуобстоятельного обсуждения изменений, которые могут повлиять на код, написанный для C++11, но скомпилированный как C++14< /эм>.


C.3.1] Разделители цифр

Разделитель цифр был введен, чтобы можно было более удобочитаемым образом записывать числовые литералы и разделять их более естественным образом.

int x = 10000000;   // (1)
int y = 10'000'000; // (2), C++14

Нетрудно заметить, что (2) намного легче читать, чем (1) в приведенном выше фрагменте, хотя оба инициализатора имеют одинаковое значение.

Потенциальная проблема, связанная с этой функцией, заключается в том, что одинарная кавычка всегда обозначала начало/конец символьного литерала в C++11, но в C++14 одинарная кавычка может либо окружать символьный литерал, либо использоваться ранее показанным способом (2).


Пример фрагмента кода, допустимый как в C++11, так и в C++14, но с разным поведением.

#define M(x, ...) __VA_ARGS__

int a[] = { M(1'2, 3'4, 5) };

// int a[] = { 5 };        <-- C++11
// int a[] = { 3'4, 5 };   <-- C++14
//                              ^-- semantically equivalent to `{ 34, 5 }`

(Примечание. Дополнительную информацию об одинарных кавычках в качестве разделителей цифр можно найти в n3781.pdf )


C.3.2] Освобождение по размеру

В C++14 появилась возможность объявить глобальную перегрузку operator delete, подходящую для освобождения размера, что было невозможно в C++11 >.

Тем не менее, Стандарт также требует, чтобы разработчик не мог объявить только одну из двух связанных функций ниже, он должен объявить либо ни одной, либо обе; что указано в [new.delete.single]p11.

void operator delete (void*) noexcept;
void operator delete (void*, std::size_t) noexcept; // sized deallocation


Дополнительная информация о потенциальной проблеме:

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

Примечание. Цитата взята с сайта n3536 — отключение размера C++

(Примечание: больше интересного можно найти в статье под названием n3536 - Освобождение размера C++, написанное Лоуренсом Кроулом )


C.3.3] constexpr функции-члены, больше не неявно const

В constexpr в C++14 внесено много изменений, но единственное изменение, которое изменит семантику между C++11 и C++14 — это постоянство функции-члена, помеченной как constexpr.

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

struct A { constexpr int func (); };

// struct A { constexpr int func () const; }; <-- C++11
// struct A { constexpr int func ();       }; <-- C++14


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


Пример фрагмента кода, допустимый как в C++11, так и в C++14, но с разным поведением

struct Obj {
  constexpr int func (int) {
    return 1;
  }

  constexpr int func (float) const {
    return 2;
  }
};

Obj const a = {}; 
int const x = a.func (123);

// int const x = 1;   <-- C++11
// int const x = 2;   <-- C++14

C.3.4] Удаление std::gets

std::gets был удален из стандартной библиотеки, поскольку он считается опасным.

Последствия этого, конечно, заключаются в том, что попытка скомпилировать код, написанный для C++11, в C++14, где используется такая функция, скорее всего, просто не скомпилируется.


(Примечание: существуют способы написания кода, который без сбоев компилируется и имеет различное поведение в зависимости от удаление std::gets из Стандартной библиотеки )

person Filip Roséen - refp    schedule 01.06.2014
comment
Еще одно изменение — тип auto i{1}; - person Jonathan Wakely; 02.06.2014
comment
@JonathanWakely std::is_same<decltype(i), std::initializer_list<int>>::value с auto i {1} также даст true в C++14; в этом аспекте не произошло никаких изменений. - person Filip Roséen - refp; 02.06.2014
comment
Еще одним изменением является добавление std::decay к реализации std::common_type. Таким образом, код типа std::common_type<int&, int&>::type f(int& x){return x;} /*...*/ int x{}; f(x) = 2; становится недействительным. - person Constructor; 02.06.2014
comment
Изменение common_type является DR, поэтому большинство поставщиков также изменят свою библиотеку C++11 (если они еще этого не сделали), и вы не сможете обнаружить никакой разницы между C++11 и C++14. в этом отношении. - person Jonathan Wakely; 02.06.2014
comment
Ваш пример для разделителей цифр недействителен С++ 11, ваш макрос требует более одного аргумента, [cpp.replace] p4 - person Jonathan Wakely; 02.06.2014
comment
@JonathanWakely Да, вы правы, конечно. Но вопрос в потенциальной возможности взлома программы C++11. - person Constructor; 02.06.2014
comment
@JonathanWakely этот конкретный пример взят прямо из N3797, я отредактирую сообщение, чтобы сделать его законным в соответствии с [cpp.replace]p4. Если нет активного DR по этому вопросу, должно быть, см.; [diff.cpp11.lex]. - person Filip Roséen - refp; 02.06.2014
comment
@FilipRoséen-refp, о боже, спасибо за предупреждение! Я открыл github.com/cplusplus/draft/issues/306. - person Jonathan Wakely; 02.06.2014
comment
@Constructor, в этом случае вы можете перечислить почти все DR, разрешенные с момента публикации C ++ 11. - person Jonathan Wakely; 02.06.2014
comment
Обратите внимание, как я уже упоминал здесь, последний черновик — N3936, а поскольку N3937 — это C++14 DIS, вероятно, имеет смысл вместо этого ссылайтесь на это. - person Shafik Yaghmour; 02.06.2014
comment
Этот последний уже вызывает проблемы с полуреализациями C++1y, которые представляют собой комбинацию заголовков C11 и C++11, где последний вызывает std::gets в прежнем, которого больше не существует: stackoverflow.com/q/17775390/560648 - person Lightness Races in Orbit; 03.06.2014
comment
В патологическом случае можно использовать SFINAE, чтобы сделать почти любое дополнение к интерфейсу в C++1y критическим изменением. Например, template<typename T, typename=typename std::enable_if< std::is_same< void(), decltype( cbegin(std::declval<T>()) ) >::value >::type> void foo(T) { std::cout << "hello "; } template<class...Ts> void foo(Ts...&&) { std::cout << "world"; } int main() { foo( std::vector<int>{} ); foo( 3 ); std::cout << "\n" } печатает hello world в C++14, но worldworld в C++11 (если я правильно написал): отсутствие интерфейса может упростить компиляцию/запуск кода на C++. - person Yakk - Adam Nevraumont; 04.06.2014
comment
@Yakk: Это не критическое изменение. Поведение правильно меняется в зависимости от наличия функции, для обнаружения которой он предназначен. - person Ben Voigt; 15.06.2014
comment
@benvoigt, за исключением того, что я могу сделать так, чтобы корректные компилируемые программы на С++ 11 не компилировались или аварийно завершали работу на С++ 1y с ним. Вы можете прочитать намерение в чеке, за исключением того, что в С++ 11 это была константа «никогда». - person Yakk - Adam Nevraumont; 15.06.2014