Enz*_*zos 21 c++ copy-constructor move-constructor move-semantics c++11
请考虑以下代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
int
int
int
copy
copy
copy
Run Code Online (Sandbox Code Playgroud)
A(10),A(20)并且A(30)是临时对象,对不对?
那么为什么复制构造函数被调用?不应该调用移动构造函数吗?
路过move(A(10)),move(A(20)),move(A(30))相反,输出为:
int
move
int
move
int
move
copy
copy
copy
Run Code Online (Sandbox Code Playgroud)
在这种情况下,调用复制或移动构造函数.
发生了什么?
And*_*dyG 14
std::vector可以从a构造std::initializer_list,并且您正在调用该构造函数.initializer_list构造的规则声明此构造函数是首选的:
构造函数是初始化列表构造函数,如果它的第一个参数是类型
std::initializer_list<E>或引用,可能std::initializer_list<E>是某些类型的cv-qualifiedE,并且没有其他参数,或者所有其他参数都有默认参数(8.3.6).[注意:初始化列表构造函数优于列表初始化<...>中的其他构造函数
此外,由于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中不需要复制省略(尽管它在C++ 17中).
有关详细信息,请参阅[class.copy]("当满足某些条件时,允许实现省略类对象的复制/移动构造...").
最后一部分是关键:
[support.initlist]说明了这一点
类型的对象
initializer_list<E>提供对类型对象数组的访问const E.
这意味着std::vector不能直接接管内存; 它必须被复制,这是你最终看到被调用的复制结构的地方.
在第二个例子中,正如Kerrek SB所述,你阻止了我之前提到的复制省略并导致了移动的额外开销.
A(10),A(20),A(30)是临时的,对吧?
正确.
那么为什么要调用复制构造函数呢?不应该调用移动构造函数吗?
不幸的是,它不可能从中移动std::initializer_list,这就是这个构造函数的std::vector用途.
通过移动(A(10)),移动(A(20)),移动(A(30))
在这种情况下,调用复制或移动构造函数.发生了什么?
因为std::move转换会阻止复制省略,所以std::initializer_list移动的元素在没有省略的情况下构建.然后矢量的构造函数从列表中复制.
| 归档时间: |
|
| 查看次数: |
1569 次 |
| 最近记录: |