C++中的复制初始化与显式初始化的理解对比

8
第一行代码为什么能编译成功,而第二行不能?
为什么可以将a作为构造函数的参数,而b不行?它们不是做着相同的事情吗?
class Foo { Foo &operator =(Foo const &); /* Disable assignment */ };

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

更新

由于似乎与编译器有关,这个问题看起来比我想象的更严重。
所以我想问题的另一个部分是,以下代码是否有效?

在GCC中它会报错,但Visual C++可以正常执行。

int main()
{
    int i = 0;
    { int *i(&i); }
    return i;
}

1
两者在GCC 4.7.2和Clang 3.2上都能正常工作(没有错误)。 - chris
1
我不确定,但是我会在几乎任何时候都选择GCC和Clang而不是MSVC。至少这可以缩小问题的范围。 - chris
1
实际上这两个都应该编译。像int a=a+100;int a(a+100);这样的代码在语法方面是正确的。但根据它们是在静态存储期还是自动存储期创建的,它们可能会引起未定义行为。 - Nawaz
1
赋值运算符在示例中有什么关系?我从来不会把这描述为“严重”,因为它只影响非常愚蠢的代码。 - Jonathan Wakely
@JonathanWakely:我有故意编写像int i(i)这样的代码,以便捕获在外部范围中定义的i的副本,如果你是这个意思的话。显然,在我问这个问题之后我才知道它们引用了同一个变量... - user541686
显示剩余7条评论
2个回答

4
在你的第一段代码中,两个声明都应该编译。GCC是正确的。Visual C++编译器有一个错误。
在第二段代码中,内部声明不应该编译。GCC也是正确的,而VC++是错误的。
在这两种情况下,GCC都是正确的。
int a=a+100;int a(a+100);这样的代码从语法角度来看是可以的。它们可能会引发未定义的行为,这取决于它们是在静态存储期还是自动存储期创建的。
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
}

请阅读与每个声明相关联的注释。
请注意,具有静态存储期的变量在编译时静态初始化为零,并且如果它们具有初始化程序,则它们也会在运行时动态初始化。但是,具有自动存储期的POD类型的变量不会被静态初始化。
有关静态初始化和动态初始化的更详细解释,请参见此处:

如果 a 已被定义为其他内容,例如一个 ,那还可以吗? - user541686
@Mehrdad:这是重新声明,对吗? - Nawaz
也许在不同的范围内,是的。但我的问题是,右侧的 a 是指旧的 a 还是新的 a - user541686
@Mehrdad:它指的是新的a:部分int a声明了一个变量,它隐藏了在外部作用域中声明的同名符号,然后=a+100是初始化器部分。 - Nawaz
哦...+1有趣,感谢您的回答。另外,这个“您是否想将此讨论移至聊天室?”的提示出现得太早了... - user541686

1
在你的第一个例子中,正如你所指出的那样,即使语法正确,行为也是未定义的。因此,编译器可以拒绝该代码(必须保证未定义的行为;在这里是有的,但如果无效的初始化从未实际执行,则不会有)。
你的第二个例子存在类型错误:声明可见于其声明符首次出现时,特别是在其自身的初始化程序中可见。MSVC++延迟了可见性:这是该编译器中已知的不符合规范的问题。例如,在EDG编译器中(具有Microsoft模式):
$ ./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); }
             ^

谢谢你再次提供的见解 :-). 现在我知道这是一个已知的 bug :-)。 - Johannes Schaub - litb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接