7

I reduced this:

struct A
{
   int * x;
   A() : x( x = new int() )
   {
   }
};

to the following:

int m = m = 3;
//or
struct X;
//...
X x = x = X();

Seems legal to me. I don't see why you'd want to do it, but is it legal? Are there cases where you'd want to do this (not the int case, I realize that's completely useless)?

Ben
  • 34,935
  • 6
  • 74
  • 113
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 6
    Do you realize that that `y = A()` is calling `operator=` on an uninitializated variable? – R. Martinho Fernandes Mar 26 '12 at 08:39
  • @R.MartinhoFernandes no. Care to expand? – Luchian Grigore Mar 26 '12 at 08:40
  • 6
    I don't get the point where you say I reduced this (example) to (completely different example). – Mr Lister Mar 26 '12 at 08:42
  • @MrLister my original question was whether it's allowed to use initializer lists like that - `x( x = new int() )` instead of `x(new int())`, like you normally would. But if the reduced version is legal, the original is too, right? – Luchian Grigore Mar 26 '12 at 08:44
  • I know about the second example being legal (even `int x = x++;` turns out to be!), but not sure about the first or the third. Need some research. Initialisers for members are not the same as creating new variables, that's what I do know. – Mr Lister Mar 26 '12 at 08:47
  • possible duplicate of [Construct object with itself as reference?](http://stackoverflow.com/questions/4368361/construct-object-with-itself-as-reference) – Bo Persson Mar 26 '12 at 16:34

4 Answers4

17

It depends on how you define "legal". It will compile; that doesn't mean that it is guaranteed to work.

Until the full statement X x = ... executes, x is uninitialized. It is not an X yet. Therefore, performing x = X() means to create a temporary X and call X::operator=(const X&) on the uninitialized variable x.

Calling a function on a non-POD class instance that has not been initialized (who's constructor has not yet been called) yields undefined behavior. If X is a POD type (or trivial in C++11), then it will work. But otherwise no.

Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Why is there a difference between POD and non-POD types? I'm not familiar with that part of the standard. Can you provide a quote? – Luchian Grigore Mar 26 '12 at 09:02
  • @LuchianGrigore Because POD types have bitwise copy semantics. – R. Martinho Fernandes Mar 26 '12 at 09:12
  • 1
    @LuchianGrigore: There's a reason I linked to that question when I said "POD class". You should read that. – Nicol Bolas Mar 26 '12 at 09:30
  • -1 for repeating what the std says. "_Calling a function on a non-POD class instance that has not been initialized yields undefined behavior._" absurd! Even with the [tag:language-lawyer] tag, you should say that the behaviour is only not defined according to the standard. – curiousguy Jul 21 '12 at 18:18
  • @curiousguy: "you should say that the behaviour is only not defined according to the standard." The standard is what *defines things*. Therefore, *by definition*, if something is undefined, it is undefined relative to the standard. And the whole point of the "language lawyer" tag is so that you give answers *according to the standard*. It's implied. – Nicol Bolas Jul 21 '12 at 19:11
  • @NicolBolas "_The standard is what defines things._" I know, thank you. "_And the whole point of the "language lawyer" tag is so that you give answers according to the standard. It's implied._" But not inane answers, or at least explain that the statement is inane and should be ignored. It absolutely clear that calling a function on an object that is not completely initialised (= the constructor has returned) is OK. Thus the statement "Calling a function on a non-POD class instance that has not been initialized yields undefined behavior." is absurd. – curiousguy Jul 21 '12 at 19:25
  • @curiousguy: "It absolutely clear that calling a function on an object that is not completely initialised (= the constructor has returned) is OK." The given code can easily be broken. For example, take `vector`. Let's say that `vector` is implemented as 3 pointers, which point to either NULL or real memory. `operator=` would need to copy the memory pointed to non-NULL pointers. However, since `x` is uninitialized, those pointers may not be NULL and won't point to real memory. Thus crashing. Why? Because you have invoked *undefined behavior* by calling `operator=` on an uninitialized `vector`. – Nicol Bolas Jul 21 '12 at 19:39
  • @curiousguy: There is nothing "inane" about saying what the standard says about object lifetimes. Especially since *that's what the user asked for:* an answer *according to the standard*, not according to what will do. – Nicol Bolas Jul 21 '12 at 19:40
  • "_Because you have invoked undefined behavior by calling operator= on an uninitialized vector_" Indeed. You cannot use an uninitialized standard object. – curiousguy Jul 21 '12 at 19:42
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14225/discussion-between-nicol-bolas-and-curiousguy) – Nicol Bolas Jul 21 '12 at 19:43
10

It's syntactically legal, but will result in undefined behavior at runtime. In a statement like:

X x = x = X();

the second = is assignment, and it assigns to an uninitialized variable. (The first = is simply the syntax for saying that what follows should be used for copy initialization; it isn't assignment.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

It's legal syntax. X is in scope for its own definition. A situation where you might want an object to refer to itself is circular linked lists, along the lines of:

struct Node {
    Node( Node *pNext_ ) : pNext( pNext_ ) {
    }
    // ...
    Node *pNext;
};

Node empty( &empty };

This empty list consists of a dummy node that has a link to itself. This is both well-defined and useful. Note that we take the address of empty, which is legal even if Node is a non-POD and hasn't been constructed yet. I don't think allowing assignment as in your your int m = m = 3; example is directly useful, but it's hard for the language to allow mine and not also allow yours.

To see how this carries over to member constructors, consider:

struct A {
    Node list;
    A() : list( &list ) {}
}  

Once the name is in scope, allowing assignment to and from it becomes possible too.

Brangdon
  • 627
  • 5
  • 7
  • Your example is completely different. – Luchian Grigore Jul 23 '12 at 14:26
  • @LuchianGrigore The question was, `"Are there cases where you'd want to do this (not the int case, I realize that's completely useless)?"` Other answers discussed the legality but said nothing about why it might be useful. My answer addresses that portion of the question. The question acknowledges that its exact examples are useless; a slightly different example was inevitable. I discuss how to get from my example to the one in the question (ie, once the name is in scope assignment becomes legal too). – Brangdon Jul 23 '12 at 18:06
  • Ok, but the edit changes the whole answer. In the original one, there was no object initialized with itself. – Luchian Grigore Jul 23 '12 at 19:08
  • Check the edit history. The answer always contained `Node empty( &empty };` in which the object `empty` is initialised with itself. My edit appended the explanation, "To see how this carries over...". But thanks for removing the downvote. – Brangdon Jul 24 '12 at 16:17
  • Oh right, sorry about that. Was misslead by `pNext( pNext_ )`. :) You're right, +1 in that case :) – Luchian Grigore Jul 24 '12 at 17:02
0
int m = m = 3;
//or
struct X;
//...
X x = x = X();

thats legal (if you define a struct which is named X, but your struct is named A, also you don't need to write struct with c++) but both is undefined behavior.

x(x = new int()) is legal but has undefined behavior too.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Pillum
  • 132
  • 9