Блог компании Инфопульс Украина / Here be dragons

в 8:08, , рубрики: c++, c++11, dragons

Просматривая материалы конференции GoingNative 2012 (которую всем программистам на С++ очень советую посмотреть), я обратил внимание на один пример кода:
Блог компании Инфопульс Украина / Here be dragons

#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 из Google о том, почему этот код работает так, как он работает. Еще раз, кто не уловил: разработчик компилятора С++ из 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! 

Вопрос к разработчикам стандарта С++11

Блог компании Инфопульс Украина / Here be dragons

Пояснения

Посмотрим внимательно на строку

f(S().n); 

Как видим, здесь создаётся экземпляр структуры S. У неё нет явного конструктора, а значит вызывается конструктор по-умолчанию. И вот тут выходит на сцену стандарт С++11 с его продвинутой поддержкой константных выражений (Generalized constant expressions). Любая функция (в том числе конструктор) в С++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 новый, его поддержка в компиляторах тоже еще сыровата. Будьте готовы к подобным сюрпризам.
Автор:


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js