std :: vector初始化移动/复制元素的构造函数

vso*_*tco 2 c++ move-semantics copy-elision c++11

我有这段代码:

#include <iostream>
#include <vector>

using namespace std;

class Foo{
public:
    Foo() noexcept {cout << "ctor" << endl;}
    Foo(const Foo&) noexcept {cout << "copy ctor" << endl;}
    Foo(Foo&&) noexcept {cout << "move ctor" << endl;}

    Foo& operator=(Foo&&) noexcept {cout << "move assn" << endl; return *this;}
    Foo& operator=(const Foo&) noexcept {cout << "copy assn" << endl; return *this;}

    ~Foo() noexcept {cout << "dtor" << endl;}
};


int main()
{   
    Foo foo;

    vector<Foo> v;
    v.push_back(std::move(foo)); 

    // comment the above 2 lines and replace by
    // vector<Foo> v{std::move(foo)}; 
}
Run Code Online (Sandbox Code Playgroud)

输出是我所期望的(编译时g++ -std=c++11 --no-elide-constructors,没有标志的相同输出)

ctor
move ctor
dtor
dtor
Run Code Online (Sandbox Code Playgroud)

现在代替使用push_back初始化直接矢量v作为

vector<Foo> v{std::move(foo)};
Run Code Online (Sandbox Code Playgroud)

我不明白为什么我得到输出:

1)(没有--no-elide-constructors)

ctor
move ctor
copy ctor
dtor
dtor
dtor
Run Code Online (Sandbox Code Playgroud)

2)(带--no-elide-constructors)

ctor
move ctor
move ctor
copy ctor
dtor
dtor
dtor
dtor
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,为什么复制ctor被调用?在第二种情况下,当编译器不执行elision时,我完全不知道为什么移动ctor被调用两次.有任何想法吗?

Pra*_*ian 8

vector<Foo> v{std::move(foo)};
Run Code Online (Sandbox Code Playgroud)

在这里,您将调用带有的向量构造函数std::initializer_list.初始化列表仅允许const访问其元素,因此vector必须将每个元素从其复制initializer_list到其自己的存储中.这就是导致调用复制构造函数的原因.

来自§8.5.4/ 5 [dcl.init.list]

类型的对象std::initializer_list<E>是从初始化列表构造的,就好像实现分配了一个临时N的类型元素const E数组,其中N是初始化列表中元素的数量.


至于额外的移动构造函数调用-fno-elide-constructors,这在几天前的另一个答案中讨论过.似乎g ++对initializer_list我在上面引用的相同部分中的标准中所示的示例实现采用了非常直观的方法.

使用clang编译时,相同的示例不会产生额外的移动构造函数调用.