Search code examples
c++language-lawyerundefined-behaviorlifetime

Contradictions on restrictions for objects outside of lifetime


[basic.life]/7 says this about outside of lifetime objects:

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated [...] The program has undefined behavior if:

  • the glvalue is used to access the object, or

[...]

It also references [class.cdtor]:

[...] For an object under construction or destruction, see [class.cdtor]. [...]

Where, in the 1 clause it is stated:

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.

"before the constructor begins execution" here means the objects is not under construction yet, which also means it's lifetime has not started.

So, according to this, as long as an object has a trivial constructor/destructor, its ok to access it's members outside of it's lifetime.

But isn't that a contradiction to what is given in [basic.life]? How does this rules work in relation to each other exactly?


Solution

  • A glvalue of class type cannot be used to perform an access, meaning a read or write. See Note 10 in [basic.lval]. You can access members of class objects, but such an access wouldn't be through a glvalue of class type directly. You would have to use the member access notation or an operation that implicitly accesses members (e.g. an implicitly defined copy constructor).

    As such, [basic.life]/7.1 applies to scalar types. It is UB to access them outside their lifetime.

    And [class.cdtor]/1 states that when a class type has a nontrivial constructor, you cannot even refer to its non-static data members before the constructor begins execution—meaning, even if you do not attempt to access those members, but merely evaluate an expression that names one (e.g., to form a pointer or reference to it), your program has UB. Same goes if the class type has a nontrivial destructor and you attempt to refer to one of its non-static data members after the destructor completes.

    There is no contradiction.

    For trivial class types to which [class.cdtor]/1 does not apply, you may refer to their members before construction begins or after destruction finishes, but if they are of scalar type, you still may not access them under [basic.life]/7.1.