【问题标题】:Understanding copy-initialization in C++, compared to explicit initialization与显式初始化相比,了解 C++ 中的复制初始化
【发布时间】:2013-01-16 10:09:08
【问题描述】:

为什么注释的第一行编译正确,而第二行编译不正确?

为什么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;
}

【问题讨论】:

  • 在 GCC 4.7.2 和 Clang 3.2 上都可以工作(错误)。
  • @chris:哦,奇怪,我用的是 Visual C++。那么,哪一个是对的?!
  • 我不确定,但我几乎每天都会猜测 GCC 和 Clang 而不是 MSVC。至少它缩小了问题的范围。
  • 实际上两者都应该编译。从语法的角度来看,像 int a=a+100;int a(a+100); 这样的代码很好。它们可能会调用 UB,具体取决于它们是在静态存储期限内创建还是在自动存储期限内创建。
  • 赋值运算符与示例中的任何内容有什么关系?而且我永远不会将其描述为“严重”,因为它只会影响非常愚蠢的代码。

标签: c++ constructor initialization


【解决方案1】:

在您的第一个代码中,两个声明都应该编译。海合会就在那里。 Visual C++ 编译器有 bug。

在第二个代码中,内部声明应该编译。 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
}

请阅读与上述每个声明相关的 cmets。

请注意,具有静态存储持续时间的变量在编译时被静态初始化为零,如果它们具有初始化器,那么它们在运行时被动态初始化。但是具有自动存储持续时间的 POD 类型的变量不是静态初始化的。

有关静态初始化与动态初始化的更多详细说明,请参阅:

【讨论】:

  • 如果a 之前被定义为其他东西,比如class,会怎样?还好吗?
  • @Mehrdad:那是重新声明,不是吗?
  • 也许在不同的范围内,是的。但我的问题是,右侧的a 是指旧的a 还是新的a
  • @Mehrdad:它指的是新的aint a 部分声明了一个变量,它隐藏了在外部范围内声明的任何同名符号,然后=a+100 是初始化部分。
  • 呵呵... +1 有趣,感谢您的回答。另一方面,这个“你想把这个讨论移到聊天吗?”事情出现得太早了……
【解决方案2】:

在您的第一个示例中,正如您所注意到的,即使语法没问题,行为也是未定义的。因此允许编译器拒绝代码(但是必须保证未定义的行为;它就在这里,但如果从未实际执行过无效的初始化,则不会如此)。

你的第二个例子有一个类型错误:只要看到它的声明器,它就可见,特别是它在它自己的初始化程序中是可见的。 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); }
             ^

【讨论】:

  • 再次感谢您的洞察力:-)。现在我知道这是一个已知的错误:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多