- PVSM.RU - https://www.pvsm.ru -
Просматривая материалы конференции GoingNative 2012 [1] (которую всем программистам на С++ очень советую посмотреть), я обратил внимание на один пример кода:
#include <iostream> struct S { int n; }; struct X { X(int) {} }; void f(void*) { std::cerr << "Pointer!n"; } void f(X) { std::cerr << "X! n"; } int main() { f(S().n); }
Сможете ли вы, не подглядывая в ответ, сказать, что напечатает эта программа и самое главное, почему?
Под катом — предположение разработчика Clang [2] из Google о том, почему этот код работает так, как он работает. Еще раз, кто не уловил: разработчик компилятора [3] С++ из Google не знает этого точно, у него всего-лишь есть предположение.
При компиляции в соответствии со стандартом С++98 этот код напечатает "X!", при компиляции в соответствии со стандартом С++11 этот код напечатает "Pointer!".
# clang++ -std=c++98 -g -o cxx11-4 cxx11-4.cpp # ./cxx11-4 X! # clang++ -std=c++11 -g -o cxx11-4 cxx11-4.cpp # ./cxx11-4 Pointer!
Посмотрим внимательно на строку
f(S().n);
Как видим, здесь создаётся экземпляр структуры S. У неё нет явного конструктора, а значит вызывается конструктор по-умолчанию. И вот тут выходит на сцену стандарт С++11 с его продвинутой поддержкой константных выражений (Generalized constant expressions [4]). Любая функция (в том числе конструктор) в С++11 может быть объявлена как всегда возвращающая одно и то же выражение. Это сделано для возможности написания вот такого кода:
int get_five() {return 5;} int some_value[get_five() + 7];
Константные выражения используются в объявлениях массивов, в перечислениях (enums), в блоках switchcase. И компилятор С++11 старается любую функцию, которая может быть константной, считать именно константной, дабы иметь возможность использовать её во всех этих местах. А что же конструктор структуры S? Ну, если он будет присваивать переменной n всегда какое-то определенное число (а стандарт этого не запрещает) — значит он тоже может быть константным выражением. А с чего бы ему присваивать n каждый раз разные значения? Присваивает ноль. Почему именно ноль? А у вас есть какое-то более умное значение на уме?
А значит, вышеуказанная строка равнозначна:
f(0);
Ну а это, как мы знаем, полностью равнозначно:
f(NULL);
А это преобразуется скорее к void*, чем к struct X (даже с соответствующим конструктором X(int)). И вот мы имеем в явном виде вызов void f(void*)! Ну и печатается "Pointer!".
Почему же это не происходит в компиляторе с поддержкой С++98? Да потому что у него нет этой самой продвинутой поддержки константных выражений. У дефолтного конструктора структуры S нет никаких причин присваивать свойству n значение ноль (стандарт не требует этого). Ну вот он этого и не делает. А значит строка f(S().n); не может быть однозначно преобразована в f(0); со всеми отсюда вытекающими последствиями.
Стандарт С++11 новый, его поддержка в компиляторах тоже еще сыровата. Будьте готовы к подобным сюрпризам.
Автор:
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/2186
Ссылки в тексте:
[1] GoingNative 2012: http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/
[2] Clang: http://ru.wikipedia.org/wiki/Clang
[3] разработчик компилятора: http://channel9.msdn.com/Events/Speakers/Chandler-Carruth
[4] Generalized constant expressions: http://en.wikipedia.org/wiki/C%2B%2B11#Generalized_constant_expressions
Нажмите здесь для печати.