Здравствуйте, Кодт, Вы писали:
... К>Любой нетривиальный конструктор, пусть и неявно определённый, — это некая функция. И вот в эту функцию пожадничали вставить лишний memset перед инициализацией отдельных полей. А уж тем более — почленное обнуление.
...
если бы вставили — им бы надо было ноги оторвать по самые уши. Я когда пул себе делал на каждое взаимодействие я чуть крышей не навернулся пока сумел эти мемсеты искоренить из кода к чертям собачьим. Помогло только объявить пустой конструктор у всех структур. Так что любителям неявных неотключаемых мемсетов срочно стоит искать себе платформу побезопаснее
имхо должно быть 2 разных синтаксиса вместо этой идиотии когда одна и та же запись Т() означает 2 разные вещи. Сделали бы
T x = {};
или еще что нибудь и не парили людям мозг этими "смысловыми перегрузками".
Для POD-типов запись T x = T(), *px = new T() — это value-initialization, приводящая к zero-initialization.
Тогда как для не-POD-типов — это default-initialization.
Поскольку derived формально является не-POD, то у него есть неявный конструктор без параметров, не инициализирующий POD-базы и члены.
Отсюда и мусор.
Статические же объекты всегда сначала обнуляются, а потом уже инициализируются в рантайме. (Компилятор вправе оптимизировать это, сразу инициализировав статическое хранилище константами).
Кстати, эта двухступенчатая инициализация является источником unspecified behavior (а то и undefined, если постараться ) при обращении к статическим объектам.
С вашего позволения, не приведу цитаты стандарта, а только укажу параграфы
— 3.6.2 Start and termination / Initialization of non-local objects
— 8.5:5 Initializers / zero-, default-, value-initializer
— 12.6.2:4 Special member functions / Initializing bases and members
Впрочем, последнее как раз и стоит процитировать.
If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
— If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a nonstatic data member of a const-qualified type, the entity class shall have a user-declared default constructor.
— Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
Здравствуйте, ilvi, Вы писали:
К>>Для POD-типов запись T x = T(), *px = new T() — это value-initialization, приводящая к zero-initialization. К>>Тогда как для не-POD-типов — это default-initialization. К>>Поскольку derived формально является не-POD, то у него есть неявный конструктор без параметров, не инициализирующий POD-базы и члены. К>>Отсюда и мусор.
I>Для не-POD без конструктора точно так будет в случае: T x = T(), *px = new T() ? I>Не должна ли рекурсивно для всех членов вызваться value-initialization ?
Сказано же: рекурсивно вызывается либо default-initialization (если элемент — не-POD), либо ничего не вызывается.
Итого, если развернуть всё дерево, ни один член не будет инициализирован, если это не делается явно.
Да, это подлянка, зашитая в стандарт, ради того, чтобы не платить за то, что не прошено.
Вот если бы в языке было две формы конструктора "без параметров" — минимальный и тотальный...
Можно ссылочку на стандарт, где написано, что там должны быть нули? Я просто давно в это не вникал, т.к. не использую переменные до их явной инициализации.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a non-static object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
Здравствуйте, Кодт, Вы писали:
К>Для POD-типов запись T x = T(), *px = new T() — это value-initialization, приводящая к zero-initialization. К>Тогда как для не-POD-типов — это default-initialization. К>Поскольку derived формально является не-POD, то у него есть неявный конструктор без параметров, не инициализирующий POD-базы и члены. К>Отсюда и мусор.
Для не-POD без конструктора точно так будет в случае: T x = T(), *px = new T() ?
Не должна ли рекурсивно для всех членов вызваться value-initialization ?
Здравствуйте, ilvi, Вы писали:
I>Здравствуйте, Кодт, Вы писали:
К>>Для POD-типов запись T x = T(), *px = new T() — это value-initialization, приводящая к zero-initialization. К>>Тогда как для не-POD-типов — это default-initialization. К>>Поскольку derived формально является не-POD, то у него есть неявный конструктор без параметров, не инициализирующий POD-базы и члены. К>>Отсюда и мусор.
I>Для не-POD без конструктора точно так будет в случае: T x = T(), *px = new T() ? I>Не должна ли рекурсивно для всех членов вызваться value-initialization ?
Здравствуйте, kvser, Вы писали:
K>Здравствуйте, ilvi, Вы писали:
I>>Здравствуйте, Кодт, Вы писали:
К>>>Для POD-типов запись T x = T(), *px = new T() — это value-initialization, приводящая к zero-initialization. К>>>Тогда как для не-POD-типов — это default-initialization. К>>>Поскольку derived формально является не-POD, то у него есть неявный конструктор без параметров, не инициализирующий POD-базы и члены. К>>>Отсюда и мусор.
I>>Для не-POD без конструктора точно так будет в случае: T x = T(), *px = new T() ? I>>Не должна ли рекурсивно для всех членов вызваться value-initialization ?
K>http://rsdn.ru/Forum/Info/FAQ.cpp.newnnew.aspx
Если это не-POD-класс-тип без явно объявленного конструктора, то в первом случае будет рекурсивно применена value-initialization к каждому подобъекту этого класса (что при этом получится — зависит о типов этих подобъектов),
Здравствуйте, Masterkent, Вы писали:
M>Это, в общем-то, известный баг VC++ — он, похоже, не учитывает правила value-инициализации C++03, а следует устаревшему стандарту 98-го года.
Аха, это разница в пункте 5.2.3 «Explicit type conversion (functional notation)» :2 стандарт (1998)(2003)
The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, whose value is determined by default-initializationwhich is value-initialized (8.5; no initialization is done for the void() case).
[Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the resulting rvalue (3.10). ]
и в пункте 12.6 «Initialization» :1
When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the
initializer has the form (), the object is initialized as specified in 8.5. [Note: if the class is a non-POD, it is default-initialized.]The object is default-initialized if
there is no initializer, or value-initialized if the initializer is ().
Тем не менее, 12.1 «Constructors» :7 — обе редакции говорят одинаково
An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object
of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the
class that would be performed by a user-written default constructor for that class with an empty meminitializer-
list (12.6.2) and an empty function body. If that user-written default constructor would be illformed,
the program is ill-formed. Before the implicitly-declared default constructor for a class is implicitly
defined, all the implicitly-declared default constructors for its base classes and its nonstatic data members
shall have been implicitly defined. [Note: an implicitly-declared default constructor has an exceptionspecification
(15.4). ]
После чего смотрим на 12.6.2:4, который я уже приводил.
Разделительная черта в стандарте (в обеих редакциях) проведена не по тривиальному/нетривиальному дефолтному конструктору, а по определённому пользователем или нет.
8.5 «Initializers» :6
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
Однако, gcc 4.3 (как и VC) смотрит именно на тривиальность/нетривиальность.
struct A { int x; };
struct B : A { int y; };
struct C : A { int z; virtual void foo() {} };
int main()
{
A a1; // VC, gcc: not initialized
A a2 = A(); // VC, gcc: value-initialize => zero-initialize
B b1; // VC, gcc: default ctor => not initialized
B b2 = B(); // VC: default ctor => not initialized
// gcc: value-initialize => zero-initialize
C c1; // VC, gcc: default ctor => not initialized
C c2 = C(); // VC, gcc: default ctor => not initialized
// vfptr они, разумеется, установили
}
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Masterkent, Вы писали:
M>>Это, в общем-то, известный баг VC++ — он, похоже, не учитывает правила value-инициализации C++03, а следует устаревшему стандарту 98-го года.
К>Аха, это разница в пункте 5.2.3 «Explicit type conversion (functional notation)» :2 стандарт (1998)(2003)
... К>и в пункте 12.6 «Initialization» :1
... К>Тем не менее, 12.1 «Constructors» :7 — обе редакции говорят одинаково
... К>После чего смотрим на 12.6.2:4, который я уже приводил.
...
Павел Кузнецов отметил, что 12.6.2/4 здесь неприменимо.
Для гарантированной инициализации можно пользоваться boost.value_init
Здравствуйте, alexeiz, Вы писали:
A>Павел Кузнецов отметил, что 12.6.2/4 здесь неприменимо.
Я, как бы, согласен с Павлом.
Однако подчёркиваю: разделение проведено по наличию-отсутствию пользовательского конструктора. А если конструктор нетривиальный, но неявно определённый — то фигня, тем не менее, наблюдается и у gcc.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>А, пардон, понял. Тогда как объяснить поведение GCC?
Очень просто.
value initialization примитивно трактуется как zero initialization, и реализуется так же: memset(&dst,sizeof(dst),0).
Причём это действие выполняется на месте создания переменной (а для статического хранилища — вообще операционной системой).
Любой нетривиальный конструктор, пусть и неявно определённый, — это некая функция. И вот в эту функцию пожадничали вставить лишний memset перед инициализацией отдельных полей. А уж тем более — почленное обнуление.
Кстати, воспроизводится для неявных конструкторов
— с нетривиальными членами
— с виртуальными функциями
И почему-то не воспроизводится для виртуального наследования http://codepad.org/0s8tKaDK
Здравствуйте, SleepyDrago, Вы писали:
SD>имхо должно быть 2 разных синтаксиса вместо этой идиотии когда одна и та же запись Т() означает 2 разные вещи. Сделали бы SD>
SD>T x = {};
SD>
SD> или еще что нибудь и не парили людям мозг этими "смысловыми перегрузками".
Смысловые перегрузки, действительно, раздражают.
А в данном случае 2 синтаксиса уже есть. Они неочевидные, и это ещё один камень в огород Страуструпа и Комитета.
// no init (POD) / default init (non-POD)
T x;
T* px = new T;
// value=zero init (POD & POD-like) / default init (non-POD)
T x = T();
T* px = new T();
Кстати, чтобы форсировать default init / no init, достаточно сделать такое
template<class T>
struct Holder
{
T data;
Holder() {}
operator T&() { return data; ]
operator T const&() { return data; }
// для панков
T* operator&() { return &data; }
T const* operator&() { return &data; }
};
То есть, не лепить пустые конструкторы во все структуры, а сделать внешнюю обёртку.
Здравствуйте, Masterkent, Вы писали:
К>>Баг gcc? Или дефект стандарта?
M>Имеет место и то и другое. Но т.к. open-std.org лежит, а этот форум опять глючит, подробное разъяснение я здесь предоставить не могу.
То есть, он уже занесён в багтрекер? Ну, хоть то радует.
А что случилось с open-std.org?
Здравствуйте, Кодт, Вы писали:
К>То есть, не лепить пустые конструкторы во все структуры, а сделать внешнюю обёртку.
Как было уже указанно, boost.value_initialized делает эту работу.
Как все же у него получается ?
Подглядев код, он делает примерно следующее:
char buffer[sizeof(T)];
memset(&buffer[0], 0, sizeof(T));
new (&buffer[0]) T();
Явный вызов memset приводит к обнулению всех полей.
А вызов конструктора вызовет конструкторы по умолчанию у членов класса у которых они есть.
Таким образом получаем нужное решение, хотя ,конечно, могли бы в стандарте обойти это более элегантно.
Здравствуйте, _nn_, Вы писали:
К>>То есть, не лепить пустые конструкторы во все структуры, а сделать внешнюю обёртку. __>Как было уже указанно, boost.value_initialized делает эту работу.
Он делает прямо противоположную работу. SleepyDrago жаловался, что ему эти memset'ы мешают (просадка производительности, видимо).
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, _nn_, Вы писали:
К>>>То есть, не лепить пустые конструкторы во все структуры, а сделать внешнюю обёртку. __>>Как было уже указанно, boost.value_initialized делает эту работу.
К>Он делает прямо противоположную работу. SleepyDrago жаловался, что ему эти memset'ы мешают (просадка производительности, видимо).
Тогда нужны две версии класса, одна с memset-ом, если требуется в компиляторе, а другая гарантированно только с конструктором.
Если же компилятор способен соптимизировать лишнее копирование, то что ему мешает соптимизировать лишнее обнуление? Не вижу поводов для упрёков авторов правил.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, SleepyDrago, Вы писали:
SD>>имхо должно быть 2 разных синтаксиса вместо этой идиотии когда одна и та же запись Т() означает 2 разные вещи. Сделали бы
... SD>> или еще что нибудь и не парили людям мозг этими "смысловыми перегрузками".
К>Смысловые перегрузки, действительно, раздражают. К>А в данном случае 2 синтаксиса уже есть. Они неочевидные, и это ещё один камень в огород Страуструпа и Комитета.
...
вы вдвоем с _nn_ меня просто убиваете. Смотрите — если кому то нужна инициализация, то он может ее написать руками 1 раз на тип. Правильно? Они напихали мемсеты почти везде и при этом оставили дырку в виде выделения на стеке, которая все равно требует явного написания тех же самых инициализаторов, чтобы не писать их каждый раз. Имхо мне нужны оба вида инициализации и задающиеся непересекающимся между собой образом.
А теперь с приходом gcc 4.4 в редхате, который исправляет "баг" с принудительной value-инициализацией pod членов классов, наступает java головного мозга. Код который всю жизнь был совместим с C по структурам придется обкладывать пустыми конструкторами, чтобы ... он был совместим с С хотябы по поведению.
К>Кстати, чтобы форсировать default init / no init, достаточно сделать такое К>
К>То есть, не лепить пустые конструкторы во все структуры, а сделать внешнюю обёртку.
Шаблонные Холдеры это к санитарам. Туда же и с предложениями держать все структуры а-ля "прямоугольник" в двойном числе.
первое правило: "меньше кода — меньше проблем".
YMMV разумеется
Здравствуйте, _nn_, Вы писали:
M>>Кстати, о следующем способе value-инициализации почему-то мало кто вспоминает.
Потому что, какая разница, как value-инициализировать, =T() или ={}.
Последняя форма, правда, неприменимо к одиночным не-POD-ам, — только к массивам.
__>А что должно происходит если есть член класса с конструктором ?
Или если есть vfptr
__>b.i — -858993460
Это который компилятор? gcc 4.5? Аха, значит, недофиксали.
Здравствуйте, SleepyDrago, Вы писали:
SD>вы вдвоем с _nn_ меня просто убиваете. Смотрите — если кому то нужна инициализация, то он может ее написать руками 1 раз на тип. Правильно? Они напихали мемсеты почти везде и при этом оставили дырку в виде выделения на стеке, которая все равно требует явного написания тех же самых инициализаторов, чтобы не писать их каждый раз. Имхо мне нужны оба вида инициализации и задающиеся непересекающимся между собой образом.
Мы-то тут при чём? Мы в комитет по стандарту не входим. Вот они тебя пускай убивают, сколько влезет.
Нужны оба способа? Так оба способа и есть. Где там дырки на стеке???
struct Foo
{
int x[1000];
// и никаких конструкторов
};
void buz(const Foo&);
void bar()
{
Foo f; // default/не инициализирована
Foo g = {}; // value-инициализирована
Foo h = Foo(); // value-инициализирована
Foo *pf = new Foo; // default/не инициализирована
Foo *ph = new Foo(); // value-инициализирована
buz(Foo()); // value-инициализирована
buz(Holder<Foo>()); // default/не инициализирована - вот здесь пришлось надстраиваться над синтаксисом
}
SD>А теперь с приходом gcc 4.4 в редхате, который исправляет "баг" с принудительной value-инициализацией pod членов классов, наступает java головного мозга. Код который всю жизнь был совместим с C по структурам придется обкладывать пустыми конструкторами, чтобы ... он был совместим с С хотябы по поведению.
В чём заключается совместимость? В том, чтобы поля были не инициализированы и содержали мусор? Можно считать, что нули — это такой специальный мусор.
SD>Шаблонные Холдеры это к санитарам. Туда же и с предложениями держать все структуры а-ля "прямоугольник" в двойном числе. SD>первое правило: "меньше кода — меньше проблем".
Нафигачить пустые конструкторы в каждую структуру — это "меньше кода"?
Конечно, зависит от соотношения, сколько типов и сколько мест, где эти переменные создаются.
Я предположил, что "делал себе пул" означает, что был сделан некий шаблон, куда подставлялись разные типы структур. Из-за чего и пришлось пропатчить все структуры, чтобы мемсетами не замучило. Ну, так можно пропатчить непосредственно шаблон.
SD>YMMV разумеется
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, SleepyDrago, Вы писали:
SD>>вы вдвоем с _nn_ меня просто убиваете. Смотрите — если кому то нужна инициализация, то он может ее написать руками 1 раз на тип. Правильно? Они напихали мемсеты почти везде и при этом оставили дырку в виде выделения на стеке, которая все равно требует явного написания тех же самых инициализаторов, чтобы не писать их каждый раз. Имхо мне нужны оба вида инициализации и задающиеся непересекающимся между собой образом.
К>Мы-то тут при чём? Мы в комитет по стандарту не входим. Вот они тебя пускай убивают, сколько влезет. К>Нужны оба способа? Так оба способа и есть. Где там дырки на стеке???
Вы явно вошли в фанклуб любителей memset'а — вот этим и убиваете. Из-за того что людей которые "непожлобились" на левый мемсет стало много вообще и началась эта затея с _изменением_ семантики уже существующего кода на плюсах.
Дырка в их абстракции тотального обнуления. Его нет когда объект просто объявлен на стеке.
...
К>Нафигачить пустые конструкторы в каждую структуру — это "меньше кода"?
Этим структурам не нужна value initialization. Никогда. Поэтому из-за этих редисок этот код стал обязательным.
предложение бороться с шаблонами с помощью шаблонов принимается. К счастью это действительно редко нужно.
К>твоя гонка за скоростью, твой и mileage
ну это имхо единственное оправдание при использовании "портабельного ассемблера наших дней". Во всех остальных случаях те кто идут по более "декларативным" путям выигрывают во всем.
Здравствуйте, SleepyDrago, Вы писали:
SD>Вы явно вошли в фанклуб любителей memset'а — вот этим и убиваете.
Ты видишь то, чего нет. Во всяком случае, в отношении меня.
Мне вообще пофиг на принудительные мемсеты. Если мне надо, я постараюсь написать так, чтобы оно гарантированно произошло, а если мне не надо, не забиваю себе голову. Какая разница, чем замусоривать, нулями или CDCDCDCD.
SD>Из-за того что людей которые "непожлобились" на левый мемсет стало много вообще и началась эта затея с _изменением_ семантики уже существующего кода на плюсах.
Это когда они не пожлобились на изменение семантики? В 1998 стандарте был дефект, — возможность трактовать как попало. Этот дефект постарались устранить (не до конца).
SD>Дырка в их абстракции тотального обнуления. Его нет когда объект просто объявлен на стеке.