为什么复制和移动构造函数一起调用?

时间:2017-08-25 11:16:53

标签: c++ c++11 copy-constructor move-semantics move-constructor

请考虑以下代码:

Consumer = Consumer Retail Food/Beverages

输出结果为:

#include <iostream>
#include <vector>
using namespace std;

class A
{
public:
     A(int) { cout << "int" << endl; }
     A(A&&) { cout << "move" << endl; }
     A(const A&) { cout << "copy" << endl; }
};

int main()
{
    vector<A> v
    {
        A(10), A(20), A(30)
    };

    _getch();
    return 0;
}

int int int copy copy copy A(10)A(20)是临时的,对吧?

那为什么复制构造函数被调用?不应该调用移动构造函数吗?

传递A(30)move(A(10))move(A(20))代替,输出为:

move(A(30))

在这种情况下,调用复制或移动构造函数。

发生了什么事?

2 个答案:

答案 0 :(得分:14)

std::vector可以从std::initializer_list构造,并且您正在调用该构造函数。 initializer_list构造的规则声明这个构造函数是主要的首选:

  

如果构造函数的第一个参数类型为std::initializer_list<E>,那么构造函数是初始化列表构造函数   或者对某些类型std::initializer_list<E>引用可能符合cv标准的E,并且有   没有其他参数或所有其他参数都有默认参数(8.3.6)。 [注意:初始化列表   构造函数优于list-initialization&lt; ...&gt;]

中的其他构造函数

另外,由于initializer_list作为在引擎盖下分配的数组的奇怪实现,std::initializer_list<E>引用的相应数组的元素被强制复制初始化(可以被忽略了:

  

类型为std::initializer_list<E>的对象是从初始化列表构造的,就像实现一样   分配了N类型E元素的数组,其中N是初始值设定项列表中的元素数。   该数组的每个元素都使用初始化列表的相应元素进行复制初始化,并且   构造std::initializer_list<E>对象以引用该数组

(上述两篇文章均来自N3337 [dcl.init.list])

但是,在您的第一个示例中,尽管名称([dcl.init] / 14),副本仍然可以 ,因此您不会看到额外的副本构造(它们也可以你可以感谢你的编译器,因为在C ++ 11中不需要 copy elision (尽管它在C ++ 17中)。

有关详细信息,请参阅[class.copy](&#34;当满足某些条件时,允许实现省略类的复制/移动构造 对象...&#34)。

最后一部分是关键:

[support.initlist]声明

  

initializer_list<E>类型的对象提供对const E类型对象数组的访问。

这意味着std::vector无法直接接管内存;它必须被复制,这是你最终看到被调用的复制结构的地方。

在第二个例子中,就像Kerrek SB所说的那样,你阻止了我之前提到的复制省略并导致了一个额外的移动开销。

答案 1 :(得分:5)

  

A(10),A(20),A(30)是临时的,对吧?

正确。

  

为什么要调用复制构造函数?不应该调用移动构造函数吗?

不幸的是,无法从=MAX(--(A3:G3=$B$7))移动,这是std::initializer_list的构造函数所使用的。

  

通过移动(A(10)),移动(A(20)),移动(A(30))

     

在这种情况下,调用复制或移动构造函数。发生了什么?

因为std::vector转换会阻止复制省略,因此std::move的元素在没有省略的情况下构建。然后,矢量的构造函数从列表中复制。