Pull to refresh
0
Инфопульс Украина
Creating Value, Delivering Excellence

Here be dragons

Reading time2 min
Views21K
Просматривая материалы конференции GoingNative 2012 (которую всем программистам на С++ очень советую посмотреть), я обратил внимание на один пример кода:

#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




Пояснения


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

f(S().n);

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

int get_five() {return 5;}
int some_value[get_five() + 7]; 

Константные выражения используются в объявлениях массивов, в перечислениях (enums), в блоках switch\case. И компилятор С++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 новый, его поддержка в компиляторах тоже еще сыровата. Будьте готовы к подобным сюрпризам.
Tags:
Hubs:
+52
Comments53

Articles

Information

Website
www.infopulse.com
Registered
Founded
1992
Employees
1,001–5,000 employees
Location
Украина