Информация об изменениях

Сообщение Re[9]: const reference nullptr от 16.07.2017 9:23

Изменено 16.07.2017 9:54 N. I.

Re[9]: const reference nullptr
prezident.mira:

NI>>А если никаких попыток создать объект вообще не предпринимается, это относится к "Before the lifetime of an object has started" или нет?


PM>Это уже попытка придраться на пустом месте. Вам показали, что Вы не правы, Вам стало от этого неприятно и Вы теперь цепляетесь за соломинку.

PM>Там всё ясно написано и оракулов, предсказывающих будущее, для описания текущего поведения стандарт не требует.

"Всё ясно" там может быть только тем, кто плохо представляет, как должны выглядеть строгие формальные правила. Не всякий указатель типа T*, указывающий на некую область памяти, где расположен объект типа T, может использоваться для доступа к этому объекту в течение его lifetime. В C++17 даже появилось специальное средство std::launder для "отмывания" указателей:

[ptr.launder]

struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object (3.8)
// because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK


То, что здесь помечено как undefined behavior — это не какое-то новшество C++17, в C++14 оно также подразумевается. Раз уж между объектом и указателем на него может быть такая хитрая связь, то почему бы не допустить, что в формулировке "Before the lifetime of an object has started" речь также идёт о какой-то привязке к объекту, который реально пытаются создать? Если мы не обязаны ничего создавать, то почему бы вместо этой галиматьи

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.


не использовать что-то вроде

Any pointer of type cv1 T* that represents the address of a storage location suitable to hold an object of type T, but does not point to any object of type cv2 T whose lifetime has started and not ended, may be used but only in limited ways.


NI>> Без авторитетного толкователя это правило можно интерпретировать как хочешь.


PM>Да, я понял. Пока не прилетит Страуструп на голубом вертолёте и не скажет: "N.I.! INDIRECTION ЧЕРЕЗ НУЛЕВОЙ УКАЗАТЕЛЬ — ЭТО НЕ UB!" то Вы всё что угодно будете интерпретировать как Вам угодно, лишь бы показаться правым во что бы то ни стало.


В таких вопросах я бы ему не стал доверять :-) Вот таким людям, как Майк Миллер, Дженс Маурер и Ричард Смит, я верю.

PM>А по факту http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315 закрыто как NAD (Not A Defect), что значит, что

PM>

PM>The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.


И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.

NI>> тут хоть как трактуй, получишь undefined behavior


PM>Если трактовать как Вам угодно, то UB будет всё, что Вам угодно. А остальным стандарт говорит http://eel.is/c++draft/basic.life#1

PM>

PM>The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:
PM>

    PM>
  • storage with the proper alignment and size for type T is obtained, and
    PM>
  • if the object has non-vacuous initialization, its initialization is complete,
    PM>


Кучка байтов из памяти не может просто так стать объектом. "An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed." Если ничто в стандарте не говорит, что в некоем storage у тебя создаётся какой-то объект, то о существовании там такого объекта говорить нельзя. malloc объектов не создаёт:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0137r1.html

Drafting note: this maintains the status quo that malloc alone is not sufficient to create an object.


PM>В C нет конструкторов, там lifetime начинается с обретением storage. И, как видно из приведённого выше пункта стандарта, C++ тут совместим с C в случае тривиальных конструкторов.


Этот пункт не имеет отношения к определению множества конструкций, которые в принципе способны создавать объекты. Похожий пример сравнительно недавно рассматривался на core рефлекторе комитета по стандартизации, вот часть обсуждения:

  Скрытый текст
Peter Dimov:
All C code in existence is dead, it just doesn't know it yet.

Bjarne Stroustrup:
Interesting. Any details? :-)

Peter Dimov:
Well as I already said,

   struct X
   {
       int a, b;
   };

   int main()
   {
       X* p = (X*)malloc( sizeof(X) );

       p->b = 1; // undefined behavior
   }


Richard Smith:
One of my main goals in writing P0137 was to clarify the existing language rules. We cannot know whether the language rules express our intent without knowledge or agreement of what the language rules even *are*. Prior to P0137, asking N language lawyers about the above example would have given you probably more than 2N different answers, due the the confused and self-contradictory wording around the description of objects and lifetime. It was never my intent to *stop* after P0137; rather, this was intended to be the starting point for refining the object model to the point where it guarantees some reasonable subset of the implementation details that users have been relying on, while not infringing too heavily on optimizability (that users have *also* been relying on).

Now we have a suitably precise framework in order to express questions such as the above and reason about their answers, we can think about exactly which cases we want to have defined behavior, and which cases we want to allow implementations to (let their users) choose between bug detection / optimization and supporting low-level memory manipulation. I'm generally of the opinion that we should require users to be explicit when doing the latter, but making exceptions for some grandfathered-in C compatibility cases should be considered.

For instance, I strongly believe the above example *should* be made to have defined behavior in C++. But no ISO C++ standard has ever guaranteed it to have defined behavior -- an implementation is, and always was, at liberty to claim that it violates [basic.lval]/8, because the user never started the lifetime of an 'int' object to store p->b.
Re[9]: const reference nullptr
prezident.mira:

NI>>А если никаких попыток создать объект вообще не предпринимается, это относится к "Before the lifetime of an object has started" или нет?


PM>Это уже попытка придраться на пустом месте. Вам показали, что Вы не правы, Вам стало от этого неприятно и Вы теперь цепляетесь за соломинку.

PM>Там всё ясно написано и оракулов, предсказывающих будущее, для описания текущего поведения стандарт не требует.

"Всё ясно" там может быть только тем, кто плохо представляет, как должны выглядеть строгие формальные правила. Не всякий указатель типа T*, указывающий на некую область памяти, где расположен объект типа T, может использоваться для доступа к этому объекту в течение его lifetime. В C++17 даже появилось специальное средство std::launder для "отмывания" указателей:

[ptr.launder]

struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object (3.8)
// because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK


То, что здесь помечено как undefined behavior — это не какое-то новшество C++17, в C++14 оно также подразумевается. Раз уж между объектом и указателем на него может быть такая хитрая связь, то почему бы не допустить, что в формулировке "Before the lifetime of an object has started" речь также идёт о какой-то привязке к объекту, который реально пытаются создать? Если мы не обязаны ничего создавать, то почему бы вместо этой галиматьи

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.


не использовать что-то вроде

Any pointer of type cv1 T* that represents the address of a storage location suitable to hold an object of type T, but does not point to any object of type cv2 T whose lifetime has started and not ended, may be used but only in limited ways.


NI>> Без авторитетного толкователя это правило можно интерпретировать как хочешь.


PM>Да, я понял. Пока не прилетит Страуструп на голубом вертолёте и не скажет: "N.I.! INDIRECTION ЧЕРЕЗ НУЛЕВОЙ УКАЗАТЕЛЬ — ЭТО НЕ UB!" то Вы всё что угодно будете интерпретировать как Вам угодно, лишь бы показаться правым во что бы то ни стало.


В таких вопросах я бы ему не стал доверять :-) Вот таким людям, как Майк Миллер, Йенс Маурер и Ричард Смит, я верю.

PM>А по факту http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315 закрыто как NAD (Not A Defect), что значит, что

PM>

PM>The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.


И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.

NI>> тут хоть как трактуй, получишь undefined behavior


PM>Если трактовать как Вам угодно, то UB будет всё, что Вам угодно. А остальным стандарт говорит http://eel.is/c++draft/basic.life#1

PM>

PM>The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:
PM>

    PM>
  • storage with the proper alignment and size for type T is obtained, and
    PM>
  • if the object has non-vacuous initialization, its initialization is complete,
    PM>


Кучка байтов из памяти не может просто так стать объектом. "An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed." Если ничто в стандарте не говорит, что в некоем storage у тебя создаётся какой-то объект, то о существовании там такого объекта говорить нельзя. malloc объектов не создаёт:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0137r1.html

Drafting note: this maintains the status quo that malloc alone is not sufficient to create an object.


PM>В C нет конструкторов, там lifetime начинается с обретением storage. И, как видно из приведённого выше пункта стандарта, C++ тут совместим с C в случае тривиальных конструкторов.


Этот пункт не имеет отношения к определению множества конструкций, которые в принципе способны создавать объекты. Похожий пример сравнительно недавно рассматривался на core рефлекторе комитета по стандартизации, вот часть обсуждения:

  Скрытый текст
Peter Dimov:
All C code in existence is dead, it just doesn't know it yet.

Bjarne Stroustrup:
Interesting. Any details? :-)

Peter Dimov:
Well as I already said,

   struct X
   {
       int a, b;
   };

   int main()
   {
       X* p = (X*)malloc( sizeof(X) );

       p->b = 1; // undefined behavior
   }


Richard Smith:
One of my main goals in writing P0137 was to clarify the existing language rules. We cannot know whether the language rules express our intent without knowledge or agreement of what the language rules even *are*. Prior to P0137, asking N language lawyers about the above example would have given you probably more than 2N different answers, due the the confused and self-contradictory wording around the description of objects and lifetime. It was never my intent to *stop* after P0137; rather, this was intended to be the starting point for refining the object model to the point where it guarantees some reasonable subset of the implementation details that users have been relying on, while not infringing too heavily on optimizability (that users have *also* been relying on).

Now we have a suitably precise framework in order to express questions such as the above and reason about their answers, we can think about exactly which cases we want to have defined behavior, and which cases we want to allow implementations to (let their users) choose between bug detection / optimization and supporting low-level memory manipulation. I'm generally of the opinion that we should require users to be explicit when doing the latter, but making exceptions for some grandfathered-in C compatibility cases should be considered.

For instance, I strongly believe the above example *should* be made to have defined behavior in C++. But no ISO C++ standard has ever guaranteed it to have defined behavior -- an implementation is, and always was, at liberty to claim that it violates [basic.lval]/8, because the user never started the lifetime of an 'int' object to store p->b.