Вопрос

Skimming through the standard draft (n3242) I found this sentence in Clause 9.2 (emphasis mine):

Non-static (9.4) data members shall not have incomplete types. In particular, a class C shall not contain a non-static member of class C, but it can contain a pointer or reference to an object of class C.

From this I argue that is fine to define a class like this:

class A {
public:
  A(A& a) : a_(a){
  }
private:
  A& a_;
};

Then in clause 8.3.2 I found the following:

A reference shall be initialized to refer to a valid object or function

Question 1: Is it permitted to define an object of this type passing its name as a reference:

A a(a);

or will this trigger undefined behavior?

Question 2: If yes, what are the parts of the standard that permit the initialization of the reference from a still-to-be-constructed object?

Question 3: If no, does this mean the definition of class A is well formed but no first object can be created without triggering UB? In this case what is the rationale behind this?

Это было полезно?

Решение

"valid object" is not defined anywhere in the standard, but it is intented to mean a region of memory with appropriate size and alignment that can contain an object of the specified type. It just means to exclude references to such things as dereferenced null pointers, misaligned regions of memory, etc. An uninitialised object is valid.

There is an open issue to clear up the wording, CWG 453.

Другие советы

n3337 § 3.8/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 12.7. Otherwise, such a glvalue refers to allocated storage (3.7.4.2), 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 (4.1) 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 (4.10) to a reference to a base class type, or

— the glvalue is used as the operand of a static_cast (5.2.9) 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 (5.2.7) or as the operand of typeid.

So, to answer your questions:

Question 1: Is it permitted to define an object of this type passing its name as a reference?

Yes. Using just the address seems not to violate this (at least for a variable put on stack).

A a(a);

or will this trigger undefined behavior?

No.

Question 2: If yes, what are the parts of the standard that permit the initialization of the reference from a still-to-be-constructed object?

§ 3.8/6 (above)


The only question that remains is how this correspond to

A reference shall be initialized to refer to a valid object or function.

The problem is in term valid object. Because § 8.3.2/4 says that

It is unspecified whether or not a reference requires storage

it seems that § 8.3.2 is problematic and should be reworded. The confusion lead to change proposed in document C++ Standard Core Language Active Issues, Revision 87 dated on 20.01.2014:

A reference shall be initialized to refer to an object or function.

Change 8.3.2 [dcl.ref] paragraph 4 as follows:

If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of memory of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined.

From n1905, 3.3.1.1

The point of declaration for a name is immediately after its complete declarator (clause 8 ) and before its initializer (if any), except as noted below.

[ Example:
int x = 12;
{ int x = x; }

Here the second x is initialized with its own (indeterminate) value.

—end example ]

My emphasis ( correct me if I am wrong ): In your example -

A a(a);

is equivalent to -

A a = a;  // Copy initialization

So, according to standard a is initialized with it's own indeterminate value. And the member is holding reference to one such indeterminate value.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top