8

Why does the first commented line compile correctly, whereas the second doesn't?

Why can a be given itself as a constructor argument, but b can't?
Aren't the two doing the same thing?

class Foo { Foo &operator =(Foo const &); /* Disable assignment */ };

int main()
{
    Foo a = a;  // OK
    Foo  b(b);  // error C2065: 'b' : undeclared identifier
}

Update

Since it seems like it's compiler-dependent, it seems like the problem is more severe than I thought.
So I guess another part of the question is, is the following code valid or no?

It gives an error in GCC but Visual C++ executes it just fine.

int main()
{
    int i = 0;
    { int *i(&i); }
    return i;
}
Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    Both work (error-wise) on GCC 4.7.2 and Clang 3.2. – chris Feb 01 '13 at 05:48
  • @chris: Oh weird, I was using Visual C++. In that case, which one is right?! – user541686 Feb 01 '13 at 05:49
  • 1
    I'm not sure, but I'd wager a guess on GCC and Clang over MSVC almost any day. At least it narrows down the problem a bit. – chris Feb 01 '13 at 05:49
  • 1
    Actually both should compile. A code like `int a=a+100;` and `int a(a+100);` is fine from syntax point of view. They might invoke UB depending on whether they're created in static storage duration or automatic storage duration. – Nawaz Feb 01 '13 at 05:49
  • @Mehrdad: I edited the title because it is not assignment initialization. The form `A a = x` is called copy-initialization, and `A a(x)` is called direct initialization. – Nawaz Feb 01 '13 at 09:06
  • perhaps vc++ doesnt lookup the identifier again after having done disambiguation lookup of the identifier to determine whether it is a type or not (if it is a type, it will be a function parameter declaration). – Johannes Schaub - litb Feb 01 '13 at 09:47
  • 1
    What has the assignment operator got to do with anything in the example? And I would never describe this as "severe" because it only affects really silly code. – Jonathan Wakely Feb 01 '13 at 11:37
  • @JonathanWakely: The assignment operator is there because I started having doubts about whether it might be somehow affecting things. And regarding it being "silly", I think it could have been perfectly legitimate code, nothing silly about it... – user541686 Feb 01 '13 at 17:05
  • When have you _intentionally_ used a variable in its own initializer in perfectly legitimate code, rather than it being an accident? – Jonathan Wakely Feb 01 '13 at 23:48
  • @JonathanWakely: I have *intentionally* written code like `int i(i)` in order to capture a copy of `i` defined in the **outer** scope, if that's what you mean. Obviously I didn't know they're referring the same variable until after I asked this question... – user541686 Feb 01 '13 at 23:54
  • Well stop doing that and get a better compiler ;-) In any case, I maintain the problem is not "severe" because it's [well known](http://stackoverflow.com/q/3173462/981959) – Jonathan Wakely Feb 01 '13 at 23:55
  • @JonathanWakely I have *intentionally* written this quiz question: http://stackoverflow.com/questions/3309042/is-this-self-initialization-valid :) – Johannes Schaub - litb Feb 02 '13 at 13:45

2 Answers2

4

In your first code, both declarations should compile. GCC is right there. Visual C++ Compiler has bug.

And in the second code, the inner declaration should not compile. GCC is right there too, and VC++ is wrong.

GCC is right in both cases.

A code like int a=a+100; and int a(a+100); is fine from syntax point of view. They might invoke undefined behavior depending on whether they're created in static storage duration or automatic storage duration.

int a = a + 100; //well-defined. a is initialized to 100
                 //a on RHS is statically initialized to 0
                 //then a on LHS is dynamically initialized to (0+100).
void f()
{
   int b = b + 100; //undefined-behavior. b on RHS is uninitialized

   int a = a + 50; //which `a` is on the RHS? previously declared one?
                   //No. `a` on RHS refers to the newly declared one.
                   //the part `int a` declares a variable, which hides 
                   //any symbol with same name declared in outer scope, 
                   //then `=a+50` is the initializer part.
                   //since a on RHS is uninitialized, it invokes UB
}

Please read the comments associated with each declaration above.

Note that variables with static storage duration is statically initialized to zero at compile time, and if they've initializer, then they're dynamically initialized also at runtime. But variables of POD types with automatic storage duration are not statically initialized.

For more detail explanation on static initialization vs dynamic initialization, see this:

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • What if `a` is previously defined as something else, like, say, a `class`? Is it still fine? – user541686 Feb 01 '13 at 05:54
  • @Mehrdad: That is redeclaration, no? – Nawaz Feb 01 '13 at 05:55
  • Maybe in a different scope, yeah. But my question is, would the `a` on the right-hand side be referring to the old `a` or the new `a`? – user541686 Feb 01 '13 at 05:56
  • @Mehrdad: It refers to the new `a` : the part `int a` declares a variable, which hides any symbol with same name declared in outer scope, then `=a+100` is the initializer part. – Nawaz Feb 01 '13 at 05:59
  • Huh... +1 interesting, thanks for the answer. On another note, this "would you like to move this discussion to chat?" thing is popping up waaaaay too early... – user541686 Feb 01 '13 at 06:10
1

In your first example, as you note, the behavior is undefined even though the syntax is okay. A compiler is therefore permitted to refuse the code (the undefined behavior must be guaranteed however; it is here, but it wouldn't be if the invalid initializations were never actually executed).

Your second example has a type error: A declaration is visible as soon as its declarator is seen, and in particular it is visible in its own initializer. MSVC++ delays the visibility: That's a known non-conformance issue in that compiler. E.g., with the EDG compiler (which has a Microsoft mode):

$ ./cfe --strict x.c
"x.c", line 4: error: a value of type "int **" cannot be used to initialize an
          entity of type "int *"
      { int *i(&i); }
               ^

1 error detected in the compilation of "x.c".
$ ./cfe --microsoft x.c
"x.c", line 4: warning: variable "i" was declared but never referenced
      { int *i(&i); }
             ^