Здравствуйте, MasterZiv, Вы писали:
MZ>Кодт пишет:
>> A>Там не отдаётся указатель. *this — это сам объект, т.е. исключение >> кидается по значению. >> ... и происходит срезка!
MZ>Ну тут по-моему очевидно, что наследников у такого исключения не MZ>должно быть. И по смыслу, и по данному коду, который на это не MZ>расчитан.
MZ>А вот как быть с двойным, вложенным throw ? MZ>Это нормально ? МОжно ?
MZ>(во время работы внешнего throw выполняется конструктор и из него MZ>срабатывает внутренний throw).
По идее конструктор выполняется ДО, а не ВО ВРЕМЯ работы внешнего throw.
Поэтому внешний throw даже не начинает выполняться (т.к. параметр для него не был создан).
Здравствуйте, MasterZiv, Вы писали:
MZ>А вот как быть с двойным, вложенным throw ? MZ>Это нормально ? МОжно ?
MZ>(во время работы внешнего throw выполняется конструктор и из него MZ>срабатывает внутренний throw).
Дело в том, что throw expr — это выражение, вычисляемое, как и положено ему, справа налево.
Сперва вычисляется expr (в нашем случае это конструктор объекта),
затем результат передаётся по значению в throw,
ну а результат throw — void — очевидно, никуда дальше не передаётся.
В нашем случае expr само кидает исключение.
Кстати, можно записать вот такой кошмар
Здравствуйте, jazzer, Вы писали:
J>по какому значению, если объект не сконструирован? J>Тут позовется конструктор копирования, у которого в параметре будет ссылка, указывающая на несуществующий объект — UB при попытке использования.
Здравствуйте, Кодт, Вы писали:
J>>Тут позовется конструктор копирования, у которого в параметре будет ссылка, указывающая на несуществующий объект — UB при попытке использования.
К>То есть,
1: The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.
А конструктор-то к этому времени ещё и недовыполнился, следовательно, объект ещё не жив. Алсо:
3.8/6:[…] before the lifetime of an object has started but after the storage which the object will occupy has been allocated […] any lvalue which refers to the original object may be used but only in limited ways. […] If an lvalue-to-rvalue conversion (4.1) is applied to such an lvalue, the program has undefined behavior; if the original object will be or was of a non-POD class type, the program has undefined behavior if:
the lvalue is used to access a non-static data member or call a non-static member function of the object, or
the lvalue is implicitly converted (4.10) to a reference to a base class type, or
the lvalue is used as the operand of a static_cast (5.2.9) (except when the conversion is ultimately to char& or unsigned char&), or
the lvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, jazzer, Вы писали:
J>>по какому значению, если объект не сконструирован? J>>Тут позовется конструктор копирования, у которого в параметре будет ссылка, указывающая на несуществующий объект — UB при попытке использования.
К>То есть, К>
Зависит от того, как написан конструктор копирования. В общем случае — UB. Не UB — только если конструктор копирования (почти) никак не использует свой аргумент.
Дело в том, что жизнь объекта начинается только после того, как отыграл конструктор (и заканчивается с началом работы деструктора) — см. соответствующую главу Object Lifetime.
Другое дело, что оно не очень понятно как должно работать в самом конструкторе/деструкторе, потому что они являются методами, а в них любое образение к члену x трактуется как (*this).x.
При это запрещено для неживого еще объекта дергать его за члены:
If the object will be or was of a non-POD class type, the program has
undefined behavior if:
— the pointer is used to access a non-static data member or call a non-static member function of the object
Что, по идее, верно и внутри конструктора и деструктора, по крайней мере, я не нашел обратного, хотя долго искал.
Получается, что к членам нельзя обращаться внутри конструктора и деструктора, что явная лажа, так что либо я не доглядел явное разрешение, либо его действительно нет (американцы, кстати, выкатили соответствующий дефект-репорт, но он не обсуждался еще: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#793.
В любом случае, к самому объекту как к целому обращаться нельзя, пока он целиком не сконструирован.
Здравствуйте, Pzz, Вы писали:
Pzz>Ну строго говоря, вы бросаете до конца не сконструированное исключение. Я бы не стал рассчитывать на то, что это всегда будет работать
Бросается копия никогда не конструируемого до конца объекта, но сама копия-то до конца сконструированая
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, Pzz, Вы писали:
Pzz>>Ну строго говоря, вы бросаете до конца не сконструированное исключение. Я бы не стал рассчитывать на то, что это всегда будет работать
AG>Бросается копия никогда не конструируемого до конца объекта, но сама копия-то до конца сконструированая
Если не считать того, что копия конструируется из несконструированного объекта, что есть UB
К>Из деструктора можно не заморачиваться с throw *this, а тупо писать там terminate(). Ибо, чего уж.
Неправда! terminate() будет только если такой деструктор будет вызван во время раскрутки стека вызванного другим исключением. И хотя подобная техника очень "плохо пахнет", думаю вполне можно бросать исключение из деструктора, который по мнению программиста не может быть вызван во время раскрутки стека :-Р
Здравствуйте, jazzer, Вы писали:
J>В любом случае, к самому объекту как к целому обращаться нельзя, пока он целиком не сконструирован.
По-моему, тут всё нормально:
12.6.2/9
Member functions (including virtual member functions, 10.3) can be called for an object under construction.
Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_-
cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly
or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result
of the operation is undefined.
[ Example:
class A {
public:
A(int);
};
class B : public A {
int j;
public:
int f();
B() : A(f()), // undefined: calls member function
// but base A not yet initialized
j(f()) { } // well-defined: bases are all initialized
};
—end example ]
Дальше:
12.7/1
For an object with a non-trivial constructor, referring to any non-static member or base
class of the object before the constructor begins execution results in undefined behavior. For an object with
a non-trivial destructor, referring to any non-static member or base class of the object after the destructor
finishes execution results in undefined behavior.
Т.е. lifitime-то начинается после завершения работы конструктора. Но фактически после начала выполнения тела конструктора с объектом можно делать всё, что и со сконструированным. По-моему, логично.
з.ы. я смотрю на N2798, драфт от 10/2008. Возможно он отличается от C++03, но я уверен, что в эту часть не было намерения вносить какие-либо принципиальные изменения, исключительно правки формулировок.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, jazzer, Вы писали:
J>>В любом случае, к самому объекту как к целому обращаться нельзя, пока он целиком не сконструирован.
R>По-моему, тут всё нормально:
R>Т.е. lifitime-то начинается после завершения работы конструктора. Но фактически после начала выполнения тела конструктора с объектом можно делать всё, что и со сконструированным. По-моему, логично.
Не с ним, а с его членами и сконструированными подобъектами (именно потому что они полностью сконструированы и их жизнь началась). С ним самим как с целым пока что ничего делать нельзя, потому что он еще неживой. Но тут оно вступает в противоречие с тем, что внутри членов-функций обращение к членам переписывается через (*this), и к этому lvalue должны применяться все ограничения про время жизни, запрещающие обращение к членам.
R>з.ы. я смотрю на N2798, драфт от 10/2008. Возможно он отличается от C++03, но я уверен, что в эту часть не было намерения вносить какие-либо принципиальные изменения, исключительно правки формулировок.
Да понятно, что имеется в виду, просто текущая формулировка, имхо, противоречит с тем, что написано про время жизни и ограничения на использование. Это и надо поменять (главу про время жизни, добавить специальную оговорку про то, что обращение к сконструированным членам разрешается, или что-то вроде того). Суть в любом случае остается той же.
Здравствуйте, jazzer, Вы писали:
J>Не с ним, а с его членами и сконструированными подобъектами (именно потому что они полностью сконструированы и их жизнь началась). С ним самим как с целым пока что ничего делать нельзя, потому что он еще неживой. Но тут оно вступает в противоречие с тем, что внутри членов-функций обращение к членам переписывается через (*this), и к этому lvalue должны применяться все ограничения про время жизни, запрещающие обращение к членам.
R>>з.ы. я смотрю на N2798, драфт от 10/2008. Возможно он отличается от C++03, но я уверен, что в эту часть не было намерения вносить какие-либо принципиальные изменения, исключительно правки формулировок.
J>Да понятно, что имеется в виду, просто текущая формулировка, имхо, противоречит с тем, что написано про время жизни и ограничения на использование. Это и надо поменять (главу про время жизни, добавить специальную оговорку про то, что обращение к сконструированным членам разрешается, или что-то вроде того). Суть в любом случае остается той же.
Тут именно про сам объект говорится, а не про члены. И функции у него можно вызывать и typeid и dynamic_cast можно.
12.6.2/9
Member functions (including virtual member functions, 10.3) can be called for an object under construction.
Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_-
cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly
or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result
of the operation is undefined.
jazzer wrote:
> R>Т.е. lifitime-то начинается после завершения работы конструктора. Но > фактически после начала выполнения тела конструктора с объектом можно > делать всё, что и со сконструированным. По-моему, логично. > > Не с ним, а с его членами и сконструированными подобъектами (именно > потому что они полностью сконструированы и их жизнь началась). С ним > самим как с целым пока что ничего делать нельзя, потому что он еще > неживой.
Ну по идее-то код конструктора (внутри скобок) == это уже пользовательский
код, не системный. Только программист знает, какое состояние объекта
у него валидное, а какое -- невалидное, соответственно он может написать
так, чтобы наш нещастный THROW был бы в том месте, где уже всё валидно,
например, в самом конце, перед скобкой. Ведь компилятор более ничего
уже не сгенерирует, никакого кода не будет (рассматриваем естественно
ситуацию БЕЗ НАСЛЕДНИКОВ, т.е. финальный класс).
Это -- если просто "по понятиям", без стандартов и формализма.
К>Конечно, надо занести! Рядом с Пабликом Морозовым.
Я только так и не понял, почему Мюнхгаузен? Тот таскал себя за косичку из болота, и летал на ядре — вроде, ничего не применимо?