c ++ 11列表初始化的不同行为

时间:2015-01-28 05:03:49

标签: c++ c++11 list-initialization

请考虑以下代码:

class A {
private:
  std::string s;
public:
  A() = delete;
  A(const A&) = delete;
  A(A&&) = delete;
  A(const std::string &a) : s(a) {}
};

现在,我想初始化一个使用列表初始化的A数组。 g ++(4.9.1)可以成功构建以下代码:

int main() {
  A arr[2] = {{"a"}, {"b"}};
  return 0;
}

但是,以下代码失败了:

class Aggr {
private:
  A arr[2];
public:
  Aggr() : arr{{"a"}, {"b"}} {}
};

错误消息是,

test.cc: In constructor ‘Aggr::Aggr()’:
test.cc:22:28: error: use of deleted function ‘A::A(A&&)’
   Aggr() : arr{{"a"}, {"b"}} {}
                            ^          
test.cc:11:3: note: declared here
   A(A&&) = delete;
   ^

也就是说,list-initializer试图调用一个移动构造函数来初始化一个类内部的数组。但是,该代码是由clang v3.5成功构建的,没有任何警告。 所以,我想知道C ++ 11(或更高版本)有关列表初始化的规则。提前谢谢。

1 个答案:

答案 0 :(得分:2)

一遍又一遍地阅读标准,我认为这是一个错误。

标准说什么?

  

8.5.1 / 2 :当初始化列表初始化聚合时,如8.5.4中所述,初始化列表的元素被采用   作为聚合成员的初始化者,正在增加   下标或成员顺序。每个成员都是从中复制初始化的   相应的初始化条款。

解释说:

  

8.5 / 14 :( ...)称为复制初始化。 [注意:复制初始化可以调用移动(12.8)。 - 后注]

但我在12.8中没有发现任何证据表明在您的具体情况下需要采取行动。

  

8.5.4 / 3 否则,如果T是类类型,则考虑构造函数。如果T有一个初始化列表构造函数,那么参数   list包含初始化列表作为单个参数;除此以外,   参数列表由初始化列表的元素组成。   枚举适用的构造函数,并选择最佳构造函数   通过重载决议(13.3)。

所以原则上你的代码应该可行!

这是一个错误吗?尝试实验方式

我注释掉了移动构造函数的删除,以便从隐式移动构造函数中受益。奇怪的是,我收到以下错误消息:

    Compilation error   time: 0 memory: 3232 signal:0

prog.cpp: In constructor 'Aggr::Aggr()':
prog.cpp:19:28: error: use of deleted function 'A::A(const A&)'
   Aggr() : arr{{"a"}, {"b"}} {}
                            ^
prog.cpp:10:3: note: declared here
   A(const A&) = delete  

所以现在他抱怨缺少复制构造函数!

更奇怪的是,我随后提供了自己的移动构造函数而不是隐式构造函数:这里它成功编译了代码!

最后,我提供了副本和移动,并添加了一些跟踪:

class A {
private:
  std::string s;
public:
  A() = delete;
  A(const A&)  { std::cout<<"copy\n";} //= delete;
  A(A&&) { std::cout<<"move\n";} //= delete;
  A(const std::string &a) : s(a) {  std::cout<<"string ctor\n";}
};

当我创建一个Aggr对象时,它只显示:

string ctor 
string ctor

显示数组成员是使用copy elision从字符串构造函数初始化的,正如我们所期望的那样。

所有这些测试都是在带有C ++ 14选项的ideone上使用gcc-9.4.2进行的。

<强>结论

相同的代码无法使用隐式移动ctor进行编译并且使用用户定义的移动ctor成功的事实看起来非常严重,就像一个错误。

移动构造函数在可用时未使用的事实强化了这种印象。

因此,我已经报告了this bug