Comments 53
Правильно ответил для C++98.
Ответ для C++11 несколько удивляет в свете введения для null значений ключевого слова nullptr. Как мне казалось, это должно было как раз оградить интерпретацию нуля от нулевого указателя.
Ответ для C++11 несколько удивляет в свете введения для null значений ключевого слова nullptr. Как мне казалось, это должно было как раз оградить интерпретацию нуля от нулевого указателя.
Ну это не объясняет, почему X(0) трактуется как литерал 0? Куда подевалась информация о типе выражения?
X(0) не трактуется, как литерал 0. Вызов X() вообще не происходит потому что при разрешении перегрузки выбирается функция f(void*), так как S().n === 0, f(void*) может принять 0 как есть, а в случае f(X) должен ещё объект создаться.
Хотя ни VC++2010 ни VC++11 таких агрессивных оптимизаций не делает и вызывает f(X).
Хотя ни VC++2010 ни VC++11 таких агрессивных оптимизаций не делает и вызывает f(X).
Давайте упростим пример.
Принципиально тут ничего не изменилось, верно?
Если память мне не изменяет, из int сделать указатель так просто нельзя. Исключение — литеральный ноль, который трактуется особым образом. Но у нас тут не литеральный ноль!
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.
В стандарте ведь появилось constexpr для обозначения константного выражения (читай указанию компилятору сделать оптимизацию). Почему же компилятор такой умный и делает оптимизацию даже без constexpr и преобразовывает 0 к nullptr (что тоже разные вещи в новом стандарте)? В итоге претензии к разработчикам стандарта С++11 или все-таки конкретного компилятора, тем более выше написали, что VC++ 2011 этого не делает?
Стандарт оставляет пространство для манёвра — компилятор вправе либо присваивать константный ноль переменной n и считать конструктор константным выражением, либо не делать этого. В этом месте допускается неопределенное поведение, и разные компиляторы генерят разный код. В этом и претензия.
Давайте будем понимать что такое корректное ПО (и компилятор в частности). Это то ПО, которое делает все, что от него требуют и в то же время не делает ничего, что от него не требуют . Если в стандарте написано, что должно происходить неявное приведение типов, но при этом не описано какой приоритет, то должна возникать ошибка «ambiguous call». Либо другой вариант — оставить поведение как в предыдущих версиях и генерировать warning. Так что не стоит пенять на стандарт.
Во-первых, если в стандарте написано «undefined behaviour», то программа вправе делать что ей хочется — разве нет?
Во-вторых, конференция (а на ней было много как разработчиков компиляторов, так и разработчиков стандарта) признала, что это должно быть как-минимум засабмичено как баг-реквест и рассмотрено на следующей сходке по стандартизации.
Во-вторых, конференция (а на ней было много как разработчиков компиляторов, так и разработчиков стандарта) признала, что это должно быть как-минимум засабмичено как баг-реквест и рассмотрено на следующей сходке по стандартизации.
>Во-первых, если в стандарте написано «undefined behaviour», то программа вправе делать что ей хочется — разве нет?
Конечно нет. Неужели вы считаете, что если написано «undefined behaviour», то компилятор может хоть винчестер отформатировать вам?
Конечно нет. Неужели вы считаете, что если написано «undefined behaviour», то компилятор может хоть винчестер отформатировать вам?
А почему инициализируется нулем?
cdcdcd как минимум, хрен знает что как максимум.
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)
Если мы создадим экземпляр S вот так: S s; то в таком случае n не будет инициализирован. (12.6.2/8)
А, таки да
Пожалуйста, давайте ссылки на новый стандарт в новом виде, например: [class.base.init] p8.
Только «S s();» — это объявление функции (по крайней мере в C++03), нужно как-то так (не уверен, будет ли тут вызван конструктор копирования или нет):
S s = S();
все просто, инициализируется нулем потому, что при S() вызывается конструктор, а конструктор int инициализирует его нулем (:
С той же конференции. Мне запомнилось, что на вопрос, какая фича стандарта 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
Часть диалога:
~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
Это core issue 903. www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#903
Рабочая группа WG21 должна объснить, применимо ли это исправление к C++11 или нет. Так что всё ещё может поменяться.
Рабочая группа WG21 должна объснить, применимо ли это исправление к C++11 или нет. Так что всё ещё может поменяться.
Чендлер входе сессии вопросов и ответов расказал про их утилиту которая определяет какие лишние include файлы в cpp. Кто-нибудь знает где ее можно найти?
Спасибо! Интересно ее реально запустить по виндой да еще над Visual C++ проектом :-)
Над *проектом* не получится, а над исходниками — должно получиться. Хотя clang не поддерживает всех Microsoft расширений, если вы найдёте что-то неподдерживоемое, сообщите, пожалуйста, в список рассылки cfe-dev.
Да я про это же, спасибо. Буду тестировать :-)
BTW, если кому интересно инструкция как сбилдить IWYU под Windows code.google.com/p/include-what-you-use/wiki/InstructionsForUsers#How_to_Build_on_Windows
Никак не удалось собрать, последняя ревизия IWYU не компилируется с последней ревизией LLVM и Clang, с 3-ей версией тоже пробовал не собирается. LLVM и Clang сами компилируются без проблем.
О, а вот и драконы!
ideone.com/1NrzT
в С++11 nullptr должно решать такие конфликты, поэтому 0 это инт а nullptr это NULL
а Clang вроде еще не поддерживает С++11
в С++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) очень подробно рассказал что происходит в этом куске кода, так что его предположение подверждено экспериментом и он точно знает что тут происходит. Не стоит паниковать.
На самом деле Chandler Carruth (http://channel9.msdn.com/Events/Speakers/Chandler-Carruth) очень подробно рассказал что происходит в этом куске кода, так что его предположение подверждено экспериментом и он точно знает что тут происходит. Не стоит паниковать.
Кстати, я вот тут подумал.
Смотрите:
Но при этом
Ну и
Но есть если мы создаём объект типа S вот так:
А что же конструктор структуры 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.
Here be dragons