以下类型有三个构造函数。请注意,其中一个采用相同类型的元素的初始值设定项列表。
struct Foo {
Foo() {
std::cout << "default ctor" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer_list<Foo>" << std::endl;
}
};
在使用initializer_list初始化对象时,我惊讶地看到复制构造函数被自动调用的次数与initializer_list中的元素一样多。之后,initializer_list构造函数称为:
int main()
{
Foo a; // default ctor
Foo b{a, a, a}; // copy ctor + copy ctor + copy ctor + initializer_list<Foo>
return 0;
}
这种行为背后的原因/理由是什么?请注意,如果 Foo 没有复制构造函数,则初始化Foo b{a, a, a}
显然是完全可能的(initializer_list构造函数将是唯一调用的构造函数(。
完整代码在这里:http://coliru.stacked-crooked.com/a/f6e28dbb66746aa2
这种行为背后的原因/理由是什么?
表达式{a, a, a}
(在您的示例中(构造一个std::initializer_list<Foo>
。
该数组的每个元素(是的,您可以像轻量级数组一样使用初始值设定项列表(都构造为对象a
的副本。
实际上,该类的复制构造函数被调用三次,以便恰好构造三个副本。
请注意,如果Foo没有复制构造函数,则初始化Foo b{a,a,a}显然是完全可能的(initializer_list构造函数将是唯一调用的构造函数(。
这不是真的。如果Foo
"没有复制构造函数",编译器将提供一个默认构造函数。因此,在这种情况下,复制构造函数仍将被调用三次,如前面的示例所示。
您只需删除默认的复制构造函数即可证明这一点。
从您的示例中:
struct Bar {
// ...
Bar(const Bar&) = delete;
};
这样,就没有复制构造函数,并且代码不会因为错误而编译:
use of deleted function 'Bar::Bar(const Bar&)'
std::cout << "d : "; Bar d{c, c, c};
实际上,不可能像往常一样构造初始值设定项列表。
结语
这并不神秘。 你想要构造一个包含三个对象的"列表"。编译器必须构造这些对象,从a
复制。