26

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?

Massimiliano
  • 7,842
  • 2
  • 47
  • 62
  • The answer to your firsrt question is no. You have not defined "a" at the moment you want to define it, its the same like the chicken and the egg. you should initialize the object with an empty reference and then set the reference t the object itself. for me this programming style sounds questionable, but i dont know what you are trying to achieve. – Sirac Feb 28 '14 at 15:17
  • lifetime of an object starts when storage is obtained and initialization is complete. If that is the definition of "valid object" ... – PlasmaHH Feb 28 '14 at 15:17
  • 1
    @Sirac: what is an "empty reference"? and how can you set a reference? – PlasmaHH Feb 28 '14 at 15:18
  • @Sirac It's not a "programming style" question, but a "language-lawyer " one. What I am trying to understand are the implications of the standard related to the parts I quoted. – Massimiliano Feb 28 '14 at 15:22
  • i was just thinking about replacing the reference with a pointer (there is a thin difference, but i dont know it just now), and set the pointer to NULL. Then you should create a method, where you can set the pointer to a given value, in your case to a pointer to itself. if you dont mind you can set the reference already in the constructor, because the object is already created then. – Sirac Feb 28 '14 at 15:22
  • @Sirac As additional question: `A a(a);` does not compiles with msvc-2013 with *`"'a' : undeclared identifier"`*, but compiles fine with gcc-4.8.2. Who is more standard compliant? – Ivan Aksamentov - Drop Feb 28 '14 at 15:54
  • i am sorry that i cannot give you an answer here, because i only used g++ up to now – Sirac Feb 28 '14 at 15:58
  • Why not define empty constructor in form `A():a_(*this){}` ? – UldisK Feb 28 '14 at 16:30
  • N3242 was not ratified. N3290 is what became the C++11 standard. – Potatoswatter Mar 03 '14 at 10:42

3 Answers3

8

"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.

5

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.

Community
  • 1
  • 1
4pie0
  • 29,204
  • 9
  • 82
  • 118
  • Using just the address means using just a pointer, right? If this is the case, that does not answer my question. – Massimiliano Feb 28 '14 at 15:25
  • no, address of a variable. Reference is an address of variable, nothing more – 4pie0 Feb 28 '14 at 15:27
  • I think, reference of a variable is just for developer, compiler will use the same address for both.. I may be wrong.. – rajenpandit Feb 28 '14 at 15:34
  • @Massimiliano The constructor is operating *on* that storage - so it must have been allocated by the time the ctor is called. – Angew is no longer proud of SO Feb 28 '14 at 15:40
  • @Angew See the example 12.7.2 containing the line `Y() : p(&x.j) { // undefined, x is not yet constructed` : from what you say this operation should be allowed, as it is just initializing an address, right? – Massimiliano Feb 28 '14 at 15:45
  • it is allocated before main begins – 4pie0 Feb 28 '14 at 15:45
  • @Massimiliano no 12.7.2 is completely different as members of x doesn't exist yet – 4pie0 Feb 28 '14 at 15:47
  • @Massimiliano 12.7.2 specifically demonstrates breaking the rule from 12.7.1, which doesn't apply in your case. – Angew is no longer proud of SO Feb 28 '14 at 15:48
  • @piotruś Is it not the same as `a` does not exist yet when I initialize a reference to itself? – Massimiliano Feb 28 '14 at 15:49
  • @Massimiliano I wouldn't say so, because in your case, the ctor of `a` already started executing, but in 12.7.2, the ctor of `x` hasn't started yet. – Angew is no longer proud of SO Feb 28 '14 at 15:49
  • @Massimiliano allocate storage is different from initializing it through constructor – 4pie0 Feb 28 '14 at 15:52
  • If one part of the standard allows a part of a program, but another disallows it, the latter wins. This answer does not explain how the OP's quote of 8.3.2 does not apply. –  Mar 03 '14 at 10:29
  • @hvd yes it answers because 8.3.2 states that reference shall be valid, and 3.8/6 states that it is consider valid in **this** example – 4pie0 Mar 03 '14 at 10:35
  • @piotruś No, 3.8 doesn't state that it is valid. The standard simply fails to provide a definition for "valid" (which is a defect, see my answer). –  Mar 03 '14 at 10:39
  • yes, I agree. It terms of what Standard says it is valid. – 4pie0 Mar 03 '14 at 10:41
  • I believe your answer is really well written and gave me a lot information (and that's why I upvoted it). Anyhow, after I read all the answers, I am more on the side of @hvd so that I am a bit struggled to accept this answer. If there was a way I would rather upvote you thrice :-) – Massimiliano Mar 03 '14 at 11:29
0

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.

Community
  • 1
  • 1
Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • `a` is definitely not initialised with its own indeterminate value. The initialisation of an `int` performs the lvalue-to-rvalue conversion, but `A`'s constructor takes a reference, so `a`'s value (insofar as it has a value at all) is unused. The point of the example is simply to show that the second `x` is in scope. –  Mar 03 '14 at 10:22