CPP Refs状态:
- 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过构造临时对象来省略复制/移动操作直接进入省略的复制/移动目标
假设我有一些测试代码:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class MyVector : public vector<T>
{
public:
MyVector()
{
cout << "MyVector()" << endl;
}
MyVector(const MyVector& right):
vector<T>(right)
{
cout << "MyVector(const MV&)" << endl;
}
MyVector(MyVector&& right) :
vector<T>(right)
{
cout << "MyVector(MV&&)" << endl;
}
};
class A
{
public:
A() = default;
A(MyVector<char> vec) :
_vec(std::move(vec))
{
cout << "A(MyVec)" << endl;
}
private:
MyVector<char> _vec;
};
MyVector<char> funcElision()
{
cout << "\nElision" << endl;
MyVector<char> tmp;
tmp.emplace_back('a');
return tmp;
}
A funcElisionExternal()
{
cout << "\nElision external test" << endl;
return A(funcElision());
}
A funcElisionInternal()
{
cout << "Elision internal test" << endl;
MyVector<char> tmp;
tmp.emplace_back('a');
return A(tmp);
}
int main()
{
auto a = funcElisionInternal();
auto b = funcElisionExternal();
}
测试的输出是:
Elision internal test
MyVector()
MyVector(const MV&)
MyVector(MV&&)
A(MyVec)
Elision external test
Elision
MyVector()
MyVector(MV&&)
A(MyVec)
End
函数elisionExternal按预期工作,但我不知道为什么elisionInternal正在进行复制操作,因为MyVec是临时对象?
答案 0 :(得分:2)
这里发生了一些事情。
funcElision
由于命名返回值优化(NRVO)而在其返回中省略了副本,这是一个特殊规则,用于命名返回值:“函数按值返回类型,返回语句的表达式为具有自动存储持续时间的非易失性对象的名称,该对象不是函数参数或catch子句参数,并且具有与函数的返回类型相同的类型(忽略顶级cv限定)。 “funcElisionExternal
中,funcElision
的返回值用作“无名临时”,可以专门删除副本(并移动后C ++ 11)。然后使用返回值优化(RVO)构造并返回A,因为它也是无名的临时值。funcElisionInternal
中,tmp
是一个名为临时的,因此只能使用NRVO删除副本。但是,它不是返回的值,并且与函数的返回签名没有相同的类型 - 它首先传递给A的构造函数。所以它不能使用NRVO。复制省略的其他规则与throw表达式和异常有关,所以这里不适用。
有关详情,请参阅cppreference's page on copy elision。
答案 1 :(得分:0)
return A(tmp);
A
使用MyVector
的构造函数按值获取它。因此,通过从glvalue复制来初始化A
的构造函数参数。这里没有可能省略,因为tmp
不是prvalue;这是一个左值。
如果直接返回,您只能从左值中删除。也就是说,如果您返回tmp
,并且返回类型为decltype(tmp)
,则可以进行省略(与funcElision
一样)。但除此之外,没有。