临时对象的复制省略
copy elision of temporary object
CPP 参考指出:
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
假设我有一些测试代码:
#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 是临时对象?
这里发生了几件事。
funcElision
正在省略其 return 中的副本,因为命名 Return 值优化 (NRVO),命名 return 值的特殊规则,当:"a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't a function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function."
- 在
funcElisionExternal
中,funcElision
的return值作为一个"nameless temporary",可以专门省略副本(并移动post-C ++11)。然后构造 A 并使用 Return 值优化 (RVO) returned,因为它也是一个无名的临时文件。
- 在
funcElisionInternal
中,tmp
是一个命名的临时文件,所以它只能用NRVO删除副本。但是,它不是被 returned 的值,并且与函数的 return 签名不具有相同的类型 - 它首先传递给 A 的构造函数。所以它不能使用NRVO。
复制省略的其他规则与抛出表达式和异常有关,因此不适用于此处。
有关更多信息,请参阅 cppreference's page on copy elision。
return A(tmp);
A
的构造函数接受了 MyVector
的值。因此,A
的构造函数参数是通过从glvalue复制来初始化的。这里不可能省略,因为 tmp
不是纯右值;它是一个左值。
如果左值被直接return编辑,则只能从左值中删除。也就是说,如果您 return 编辑了 tmp
,并且 return 类型是 decltype(tmp)
,则可以进行省略(就像您对 funcElision
所做的那样)。但除此之外,没有。
CPP 参考指出:
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
假设我有一些测试代码:
#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 是临时对象?
这里发生了几件事。
funcElision
正在省略其 return 中的副本,因为命名 Return 值优化 (NRVO),命名 return 值的特殊规则,当:"a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't a function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function."- 在
funcElisionExternal
中,funcElision
的return值作为一个"nameless temporary",可以专门省略副本(并移动post-C ++11)。然后构造 A 并使用 Return 值优化 (RVO) returned,因为它也是一个无名的临时文件。 - 在
funcElisionInternal
中,tmp
是一个命名的临时文件,所以它只能用NRVO删除副本。但是,它不是被 returned 的值,并且与函数的 return 签名不具有相同的类型 - 它首先传递给 A 的构造函数。所以它不能使用NRVO。
复制省略的其他规则与抛出表达式和异常有关,因此不适用于此处。
有关更多信息,请参阅 cppreference's page on copy elision。
return A(tmp);
A
的构造函数接受了 MyVector
的值。因此,A
的构造函数参数是通过从glvalue复制来初始化的。这里不可能省略,因为 tmp
不是纯右值;它是一个左值。
如果左值被直接return编辑,则只能从左值中删除。也就是说,如果您 return 编辑了 tmp
,并且 return 类型是 decltype(tmp)
,则可以进行省略(就像您对 funcElision
所做的那样)。但除此之外,没有。