Inspired from this question.
struct E {};
E e;
E f(e); // Accesses e?
To access is to
read or modify the value of an object<
I think it does not access the object, though a valid object is required to be present.
E f(e);
This calls E
's implicitly defined constructor E::E(const E&)
. Obviously the body of this constructor is empty (because there is nothing to do). So if anything happens, it must happen during argument passing, i.e. during the initialization of const E&
from e
.
It is self-evident that this initialization does not modify e
. Now, to read the value of e
, a lvalue-to-rvalue conversion must take place. However, the standard actually says that this conversion does not take place during direct reference binding1. That is to say, no read is performed.
However, the standard does require that a reference must be initialized to refer to a valid object or function2 (though this is subject to CWG 453), so things like E f(*reinterpret_cast<E*>(nullptr));
will be ill-formed.
1. This is done by not normatively requiring such conversion, and further strengthened by the non-normative note in [dcl.init.ref].
2. [dcl.ref].
I think that the part of the standard that describes the most precisely what performs an access is [basic.life]. In this paragraph it is explained what can be done with a reference that refers to, or a pointer that point to, an object which is out of its lifetime period. Everything that is authorized to do with such entities does not perform an access to the object value since such value does not exist (otherwise the standard would be inconsistent).
So we can take a more drastic example, if this is not undefined behavior, so there are no access to e
in your example code (accordingly to the reasonning above):
struct E{
E()=default;
E(const E&){}
};
E e;
e.~E();
E f(e);
Here e
is an object whose lifetime has ended but whose storage is still allocated. What can be done with such a lvalue is described in [basic.life]/6
Similarly, 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 glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:
an lvalue-to-rvalue conversion ([conv.lval]) is applied to such a glvalue,
the glvalue is used to access a non-static data member or call a non-static member function of the object, or
the glvalue is implicitly converted ([conv.ptr]) to a reference to a base class type, or
the glvalue is used as the operand of a static_cast ([expr.static.cast]) except when the conversion is ultimately to cv char& or cv unsigned char&, or
the glvalue is used as the operand of a dynamic_cast ([expr.dynamic.cast]) or as the operand of typeid.
None of the cited point above does happen inside E
copy constructor so the example code in this answer is well defined, which implies that there have been no access to the value of the destroyed object. So there is no access to e
in your example code.