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

Cho*_*ang 13 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) {}
};
Run Code Online (Sandbox Code Playgroud)

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

int main() {
  A arr[2] = {{"a"}, {"b"}};
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,它失败了以下代码:

class Aggr {
private:
  A arr[2];
public:
  Aggr() : arr{{"a"}, {"b"}} {}
};
Run Code Online (Sandbox Code Playgroud)

错误消息是,

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;
   ^
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*phe 2

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

\n\n

标准怎么说?

\n\n
\n

8.5.1/2:当聚合由初始值设定项列表初始化时(如 8.5.4 中所指定),初始值设定项列表的元素将被\n 作为聚合成员的初始值设定项,按下标或成员顺序递增\n 。每个成员都是从相应的初始化子句复制初始化的。

\n
\n\n

据解释:

\n\n
\n

8.5/14 : (...) 称为复制初始化。[注意:复制初始化可能会调用移动(12.8)。\xe2\x80\x94结束注]

\n
\n\n

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

\n\n
\n

8.5.4/3否则,如果 T 是类类型,则考虑构造函数。如果 T 有一个初始化列表构造函数,则参数列表由初始化列表作为单个参数组成;否则,参数列表由初始值设定项列表的元素组成。枚举适用的构造函数,并通过重载决议选择最好的构造函数(13.3)。

\n
\n\n

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

\n\n

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

\n\n

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

\n\n
    Compilation error   time: 0 memory: 3232 signal:0\n\nprog.cpp: In constructor \'Aggr::Aggr()\':\nprog.cpp:19:28: error: use of deleted function \'A::A(const A&)\'\n   Aggr() : arr{{"a"}, {"b"}} {}\n                            ^\nprog.cpp:10:3: note: declared here\n   A(const A&) = delete  \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

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

\n\n
class A {\nprivate:\n  std::string s;\npublic:\n  A() = delete;\n  A(const A&)  { std::cout<<"copy\\n";} //= delete;\n  A(A&&) { std::cout<<"move\\n";} //= delete;\n  A(const std::string &a) : s(a) {  std::cout<<"string ctor\\n";}\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
string ctor \nstring ctor\n
Run Code Online (Sandbox Code Playgroud)\n\n

显示数组成员是按照我们的预期使用复制省略从字符串构造函数初始化的。

\n\n

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

\n\n

结论

\n\n

事实上,相同的代码无法使用隐式移动向量进行编译,而使用用户定义的移动向量可以成功编译,这看起来非常像一个错误。

\n\n

事实上,移动构造函数在可用时并未被使用,这一事实强化了这种印象。

\n\n

因此,我报告了这个错误

\n