31

Possible Duplicate:
int var = 1; void main() { int i = i; }

The following code can pass compiling under both g++ and Visual C++. Why is it legal? It looks unreasonable, and may cause hidden bugs.

int main() {
  int i = i;
}
Community
  • 1
  • 1
Aerodonkey
  • 289
  • 3
  • 6

4 Answers4

47

EDIT: It's syntactically legal, but results in undefined behavior if you use x.

It's not legal because you're assigning an uninitialized variable with another (well, the same) uninitialized variable. Just because it compiles doesn't mean it's legal. It's valid C++ syntax, yes, but not legal.

The right hand side of the assignment operator must be fully evaluated at the time of the assignment. In this case, that's i, which isn't initialized.

Credits to Steve Jessop, who dug up the quote:

4.1/1, lvalue-to-rvalue conversion

[...] if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

casperOne
  • 73,706
  • 19
  • 184
  • 253
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • That argument is circular, having a variable whose value is undefined is certainly not illegal. Compare it to just plain `int i;` which also leaves `i` with an undefined value. – unwind Jun 25 '12 at 09:05
  • @nhahtdh that doesn't mean it's legal. – Luchian Grigore Jun 25 '12 at 09:05
  • Luchian, it compiles, and there's nothing in the spec that disallows it. – OmnipotentEntity Jun 25 '12 at 09:06
  • @unwind yes, and assigning that `i` to something else is also not legal. So why would this be? – Luchian Grigore Jun 25 '12 at 09:06
  • 2
    @LuchianGrigore: Legal in what sense? I get the idea that it's totally wrong to do so, though. But the compiler happily compiles it. – nhahtdh Jun 25 '12 at 09:07
  • @nhahtdh the compiler also compiles returning a local variable by reference, but it's also not legal. – Luchian Grigore Jun 25 '12 at 09:08
  • 3
    It's 100% correct---the code is illegal. The answer might be slightly better if he's mentioned the keywords "undefined behavior", but he's covered all of the important points: valid syntax, but not legal; `i` evaluated before being initialized; etc. – James Kanze Jun 25 '12 at 09:08
  • 8
    @nhahtdh In the sense defined in the standard, what else? **Whether it compiles or not is irrelevant for legality.** Otherwise we’d have no undefined behaviour. **Stop (down)voting if you have no clue, people!** – Konrad Rudolph Jun 25 '12 at 09:09
  • @Luchian To be honest, I’m not actually sure whether this code is legal or not – reading the value of `x` afterwards certainly is UB. This initialisation? No idea. But all the arguments mentioned here claiming that this is legal are just wrong. – Konrad Rudolph Jun 25 '12 at 09:12
  • @KonradRudolph the only arguments here are that the compiler accepts it. :) Which is definitely not a valid one. – Luchian Grigore Jun 25 '12 at 09:13
  • @KonradRudolph I actually posted a similar question - http://stackoverflow.com/questions/9868921/is-x-x-x-x-legal-c - that's why I remembered this isn't illegal. If it is however, I'll have to go back to that. – Luchian Grigore Jun 25 '12 at 09:14
  • 9
    If it helps: 4.1/1, lvalue-to-rvalue conversion, "if the object is uninitialized, a program that necessitates this conversion has undefined behavior". You cannot read uninitialized objects, so unless you care to argue that `i` is not uninitialized when it is read in its own initializer, I don't think there's a huge amount of debate what the standard says about this case. – Steve Jessop Jun 25 '12 at 09:19
  • @SteveJessop thank you, I hope you don't mind if I put this in the answer. – Luchian Grigore Jun 25 '12 at 09:20
  • 7
    I think you must clasrify what exactly you mean by _valid_ and _legal_. Many people seem to confuse the two words. – Salman A Jun 25 '12 at 09:36
  • 1
    @Salman: Indeed, the terms need some clarification. The code may be treated illegal only if it is actually executed. For example, if main() isn't defined in global scope and is never called, the code is absolutely legal. Any cases of UB in unevaluated operands are legal, etc. – user396672 Jun 25 '12 at 11:10
  • I'm pretty sure the code is *legal* in my jurisdiction. It is also *well-formed* as far as the C++ standard is concerned. The behavior of such program, however, is not defined by the standard (i.e. it invokes *undefined behavior*). – avakar Jun 25 '12 at 13:13
  • I'm not clear on this concept "legal" in program text. Where is the law book that defines what is legal and what is not? Do the SO police prosecute you if you code something that's not legal? – Mike Dunlavey Jun 25 '12 at 13:22
17

The reason it's allowed by the syntax is that there are some odd cases where you might conceivably want to use a variable by pointer or reference in its own initializer:

struct ThingManager {
    void *thing;
    ThingManager(void *thing) : thing(thing) {}
    void Speak() {
        if (thing == (void*)this) {
            std::cout << "I'm managing myself\n";
        } else {
            std::cout << "I'm managing " << thing << "\n";
        }
    }
};

ThingManager self_manager(&self_manager);
ThingManager other_manager(&self_manager);

So C++ lets you refer to an object in its own initializer expression (its name is in scope). Then as ever in C++, it's your problem to make sure you don't actually use an uninitialized value (your example, int i = i; does use an uninitialized value).

You compiler might help with identifying uses of uninitialized values, but the standard doesn't require it to.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
8

You can let g++ warn you about this use case with -Winit-self (in conjunction with -Wuninitialized), and if you treat warnings as errors, it should satisfy your itch.

This technique of using the copy constructor to self-initialize is sometimes used to suppress a global object's default constructor/initializer from executing. This may be necessary if the global object's default constructor is just to 0 initialize the object, but the object got used before the constructor would have been executed. As a throwback to C, globals are 0 initialized at program start, and then the C++ runtime begins to execute global constructors. For those narrow cases where the defined constructor that would have executed is only to 0 out the object, the self initialization does not do any harm.

In the general case, copy constructor self-initialization is bad practice, since it generally would cause the same sorts of problems that using an uninitialized variable would cause (that is, undefined behavior). In the particular example in the OP's question, i is local to main, and is therefore uninitialized. The result of reading an uninitialized variable is always undefined behavior.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • It doesn't seem to generate any warning with `-Winit-self` for me, though. – nhahtdh Jun 25 '12 at 09:09
  • _If_ the object has a user defined type, and the constructor takes the instance by reference, and never dereferences it (in the constructor), then the code is legal. (The user defined constructor could save the address, for example.) – James Kanze Jun 25 '12 at 12:32
  • @JamesKanze: Yes, I read your interesting example. But, I was only talking about the copy constructor. Thanks and regards – jxh Jun 25 '12 at 15:08
3

You can use any previously declared variable as an initialiser of another variable.

In this case as soon as the compiler parses int i it adds that to the symbol table, so when it sees the = i initialiser, the symbol can be resolved from the preceding declaration.

It is not an error because the compiler can make sense of it in that it can generate code that unambiguously does exactly what the source code specifies, even if it is semantically suspect. The philosophy of C and C++ is to compile anything that can possibly be compiled syntactically. Semantic errors generally issue only warnings, and only then if such warnings are enabled.

Clifford
  • 88,407
  • 13
  • 85
  • 165