Pull to refresh

Comments 53

Правильно ответил для C++98.
Ответ для C++11 несколько удивляет в свете введения для null значений ключевого слова nullptr. Как мне казалось, это должно было как раз оградить интерпретацию нуля от нулевого указателя.
Совместимость с C++98 всё равно остаётся в C++11. А значит, 0 по-прежнему может восприниматься как нулевой указатель
Ну это не объясняет, почему X(0) трактуется как литерал 0? Куда подевалась информация о типе выражения?
X(0) не трактуется, как литерал 0. Вызов X() вообще не происходит потому что при разрешении перегрузки выбирается функция f(void*), так как S().n === 0, f(void*) может принять 0 как есть, а в случае f(X) должен ещё объект создаться.

Хотя ни VC++2010 ни VC++11 таких агрессивных оптимизаций не делает и вызывает f(X).
Давайте упростим пример.
const int zero=0;
f(zero);

Принципиально тут ничего не изменилось, верно?
Если память мне не изменяет, из int сделать указатель так просто нельзя. Исключение — литеральный ноль, который трактуется особым образом. Но у нас тут не литеральный ноль!
У меня при компиляции вашего кода с const int zero вызывается f(void*), причем даже при компиляции VS2008 и gcc 4.5.3, которые не поддерживают С++11.
Не поленился залезть в стандарт:
4.10/1
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer valueof that type

Так что особым способом трактуется не литеральный ноль, а константный ноль.
Спасибо.
Но тогда в примере из статьи нет никакой мистики — просто компилятор способен заглядывать чуть глубже. Думаю разработчик компилятора из гугла это тем более понимает)
Вы хотите сказать почему 0 трактуется как X(0) в C++98? Это очень просто — у конструктора X не объявлен explicit.
Нет. Я не обратил внимания, что там не просто f(X()) а f(X().n).
В стандарте ведь появилось constexpr для обозначения константного выражения (читай указанию компилятору сделать оптимизацию). Почему же компилятор такой умный и делает оптимизацию даже без constexpr и преобразовывает 0 к nullptr (что тоже разные вещи в новом стандарте)? В итоге претензии к разработчикам стандарта С++11 или все-таки конкретного компилятора, тем более выше написали, что VC++ 2011 этого не делает?
Стандарт оставляет пространство для манёвра — компилятор вправе либо присваивать константный ноль переменной n и считать конструктор константным выражением, либо не делать этого. В этом месте допускается неопределенное поведение, и разные компиляторы генерят разный код. В этом и претензия.
Давайте будем понимать что такое корректное ПО (и компилятор в частности). Это то ПО, которое делает все, что от него требуют и в то же время не делает ничего, что от него не требуют . Если в стандарте написано, что должно происходить неявное приведение типов, но при этом не описано какой приоритет, то должна возникать ошибка «ambiguous call». Либо другой вариант — оставить поведение как в предыдущих версиях и генерировать warning. Так что не стоит пенять на стандарт.
Во-первых, если в стандарте написано «undefined behaviour», то программа вправе делать что ей хочется — разве нет?
Во-вторых, конференция (а на ней было много как разработчиков компиляторов, так и разработчиков стандарта) признала, что это должно быть как-минимум засабмичено как баг-реквест и рассмотрено на следующей сходке по стандартизации.
>Во-первых, если в стандарте написано «undefined behaviour», то программа вправе делать что ей хочется — разве нет?
Конечно нет. Неужели вы считаете, что если написано «undefined behaviour», то компилятор может хоть винчестер отформатировать вам?
Слова undefined behaviour значат «отправляет начальнику email с оскорблениями», это все знают
А почему инициализируется нулем?
cdcdcd как минимум, хрен знает что как максимум.
Забить ноль, во-первых, может быть быстрее (XOR n,n), во-вторых логично (вон С# всегда так по-дефолту делает).
Быстрее — забить что надо, или не забивать ничего, если не надо.
В статье был описан мотив компилятора: по-возможности получать константные выражения из функций (в том числе конструктора). Вы предлагаете не делать этого и терять возможность использовать const expression в объявлении массивов, enum-ов и т.д.? Или действительно присваивать какое-то число отличное от нуля? Аргументы?
А почему инициализируется нулем?

Потому что
8.5/10
An object whose initializer is an empty set of parentheses, i.e.,(), shall be value-initialized.

8.5/7
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.

8.5/5
Tozero-initialize an object or reference of type T means:
— if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class
subobject is zero-initialized and padding is initialized to zero bits;
Поскольку в примере нет инициализатора для поля n, то данное поле не должно инициализироваться — т.е. содержать конкретное значение.

Неудачный вобщем пример в котором намешали несколько необдуманных допущений сделанных разработчиками компилятора.
Если мы создаём экземпляр вот так: S s(); (ну или как в примере создаём безымянный временный объект: S()), то весь объект будет zero-initialized, в том числе и поле n. (8.5/10)
Если мы создадим экземпляр S вот так: S s; то в таком случае n не будет инициализирован. (12.6.2/8)
Пожалуйста, давайте ссылки на новый стандарт в новом виде, например: [class.base.init] p8.

В старом стандарте тоже есть class.base.init, просто мне как-то привычнее по номерам почему-то. В оглавлении быстрее находится. Буду и такие и такие приводить, если не забуду :)
Только «S s();» — это объявление функции (по крайней мере в C++03), нужно как-то так (не уверен, будет ли тут вызван конструктор копирования или нет):
S s = S();
Угу, вы правы. Но в примере временный объект всё равно zero-initialized, что я в общем то и хотел сказать.
все просто, инициализируется нулем потому, что при S() вызывается конструктор, а конструктор int инициализирует его нулем (:
Конструктор вызывается всегда. Просто default-initialization, когда инициализатор не указан, trivial constructor будет default-initializeить мемберов, а для int default initialization == no initialization. 12.6.2/8.
С той же конференции. Мне запомнилось, что на вопрос, какая фича стандарта C++11 самая трудоемкая для реализации, разработчик clang сказал, что это constexpr. Саттера впечатлило, когда он узнал, кто и сколько времени ее делал.
Договорите пожалуйста, кто и сколько времени, интересно ведь. Спасибо.
Вы такой благодарный, воспитанный такой.
Я услышал это во время второй сессии вопросов и ответов: Interactive Panel: Ask Us Anything!. Теме constexpr посвящен отрезок с 17:00 до ~22:00

Часть диалога:
~20:15
Chandler Carruth:… Richard Smith has been working on constexpr for over six months now I think…
Herb Sutter: Richard has!?
Chandler Carruth: Yes
Herb Sutter: Oh!
Chandler Carruth: Oh no, no. I don't know about full time… But we are not done yet. It that hard. It is harder then variadic templates, I believe it is actually harder then lambdas. It is one of the most challenging to implement correctly features in entire C++11
Bjarne Stroustrup: It is interesting to note, that it is one of these features, that was made more complicated over the dead bodies of its proposers and initial implementors during the standartisation
Чендлер входе сессии вопросов и ответов расказал про их утилиту которая определяет какие лишние include файлы в cpp. Кто-нибудь знает где ее можно найти?
Спасибо! Интересно ее реально запустить по виндой да еще над Visual C++ проектом :-)
Над *проектом* не получится, а над исходниками — должно получиться. Хотя clang не поддерживает всех Microsoft расширений, если вы найдёте что-то неподдерживоемое, сообщите, пожалуйста, в список рассылки cfe-dev.
Да я про это же, спасибо. Буду тестировать :-)
Никак не удалось собрать, последняя ревизия IWYU не компилируется с последней ревизией LLVM и Clang, с 3-ей версией тоже пробовал не собирается. LLVM и Clang сами компилируются без проблем.
Удалось все таки собрать с ревизией 330 и 3ей версией LLVM и Clang. Правда непонятно как указать пути к инклюдам используемым в проекте.
ideone.com/1NrzT

в С++11 nullptr должно решать такие конфликты, поэтому 0 это инт а nullptr это NULL
а Clang вроде еще не поддерживает С++11
$ cat foo.cpp
#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);
}
$ g++-4.6.2 -std=c++98 foo.cpp && ./a.out
X! 
$ g++-4.6.2 -std=c++0x foo.cpp && ./a.out
Pointer!
$ g++-4.5.3 -std=c++98 foo.cpp && ./a.out
X! 
$ g++-4.5.3 -std=c++0x foo.cpp && ./a.out
X! 
$ g++-4.4.5 -std=c++98 foo.cpp && ./a.out
X! 
$ g++-4.4.5 -std=c++0x foo.cpp && ./a.out
X! 


Generalized constant expressions появились в GCC 4.6.

nullptr конечно должен, но:
4.10 [conv.ptr]
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to
zero or a prvalue of type std::nullptr_t.

> Еще раз, кто не уловил: разработчик компилятора С++ из Google не знает этого точно, у него всего-лишь есть предположение.

На самом деле Chandler Carruth (http://channel9.msdn.com/Events/Speakers/Chandler-Carruth) очень подробно рассказал что происходит в этом куске кода, так что его предположение подверждено экспериментом и он точно знает что тут происходит. Не стоит паниковать.
Примерно за 9 минут и 20 секунд до конца видео один звучит вопрос из зала: «объясните данный пример, я его не понимаю». Ответ докладчика: «Ну, я его не понимаю тоже».
Я ничего не придумал и не переврал. Возможно, он это сказал «к слову», ради фигуры речи — но это было.
Кстати, я вот тут подумал.
А что же конструктор структуры S? Ну, если он будет присваивать переменной n всегда какое-то определенное число (а стандарт этого не запрещает) — значит он тоже может быть константным выражением.
Тут спорный вопрос, запрещает стандарт или не запрещает.

Смотрите:
[dcl.constexpr] (7.1.5) /4
The definition of a constexpr constructor shall satisfy the following constraints:
every non-static data member and base class sub-object shall be initialized

Но при этом
[class.base.init] (12.6.2) /8
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has abrace-or-equal-initializer, the entity is initialized as specified in 8.5;
— otherwise, if the entity is a variant member (9.5), no initialization is performed;
— otherwise, the entity is default-initialized (8.5).

Ну и
[dcl.init] (8.5) /6
To default-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, no initialization is performed.

Но есть если мы создаём объект типа S вот так:
S s;
то для n у нас no initialization is performed, а не противоречит ли это тому, что every non-static data member shall be initialized? Если противоречит, то такой конструктор не может быть constexpr.
Sign up to leave a comment.