我打算调用 initializer_list 构造函数,如果存在复制构造函数,则会预先调用它:为什么?

I intend to call initializer_list constructor, and copy constructor is called beforehand if it exists: why?

以下类型具有三个构造函数。请注意,其中一个采用相同类型的元素的初始化列表。

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;
}

这种行为背后的 reasons/justifications 是什么?请注意,如果 Foo 没有复制构造函数,初始化 Foo b{a, a, a} 显然是完全可能的( initializer_list 构造函数将是唯一被调用的)。

完整代码在这里:http://coliru.stacked-crooked.com/a/f6e28dbb66746aa2

What are the reasons/justifications behind that behavior?

表达式 {a, a, a}(在您的示例中)构造了一个 std::initializer_list<Foo>.

该数组的每个元素(是的,您可以像轻量级数组一样使用初始化列表)被构造为对象 acopy

实际上,class 的 复制构造函数 被调用了三次,以便恰好构造三个副本。


Note that if Foo had no copy constructor, the initialization Foo b{a, a, a} would apparently be perfectly possible (the initializer_list constructor would be the only one called).

这不是真的。如果Foo "has no copy constructor",编译器会提供一个默认的。所以在那种情况下,复制构造函数仍然会被调用三次,就像前面的例子一样。

你可以证明它只是删除默认的复制构造函数。

根据你的例子:

struct Bar {
  // ...
  Bar(const Bar&) = delete;
};

这样就没有拷贝构造函数,代码编译不通过,报错:

use of deleted function 'Bar::Bar(const Bar&)'
     std::cout << "d : "; Bar d{c, c, c};

确实,无法像往常一样构造初始化列表。


最终结论

这没有什么神秘之处。 你想构造一个 "list" 三对象。编译器必须构造那些从 a 复制的对象。