我想知道在C ++ 11中我还需要在参数中使用const引用。我不完全理解移动语义,但我认为这是一个合法的问题。 此问题仅适用于const引用替换正在制作的副本而仅需要“读取”该值(例如const成员函数的使用)的情况。
通常我会编写一个(成员)函数,如下所示:
#include <vector>
template<class T>
class Vector {
std::vector<T> _impl;
public:
void add(const T& value) {
_impl.push_back(value);
}
};
但是我认为可以安全地假设编译器会使用移动语义来优化它,如果我这样写它并且class T
ofcourse实现了移动构造函数:
#include <vector>
template<class T>
class Vector {
std::vector<T> _impl;
public:
void add(T value) {
_impl.push_back(value);
}
};
我是对的吗?如果是这样,假设它可以在任何情况下使用是否安全?如果没有,我想知道哪个。 这将使生活变得更加容易,因为我不必为基本类型实现类专门化,例如,它看起来更清晰。
答案 0 :(得分:29)
您提出的解决方案:
void add(T value) {
_impl.push_back(value);
}
需要进行一些调整,因为这样你总是会执行value
的一个副本,即使你将rvalue传递给add()
(如果传递左值,也会传递两个副本):since { {1}}是一个左值,当你将它作为参数传递给value
时,编译器不会自动从它移动。
相反,你应该这样做:
push_back
这样做更好,但对模板代码仍然不够好,因为您不知道void add(T value) {
_impl.push_back(std::move(value));
// ^^^^^^^^^
}
移动是否便宜或昂贵。如果T
是这样的POD:
T
然后移动它将不会比复制它更快(事实上,移动它将与复制它相同的事情)。因为您的通用代码应该避免不必要的操作(这是因为某些类型的操作可能很昂贵),所以您无法承担按值获取参数。
一种可能的解决方案(C ++标准库采用的解决方案)是提供两个struct X
{
double x;
int i;
char arr[255];
};
重载,一个采用左值引用,另一个采用右值引用:
add()
另一种可能性是提供一个(可能是SFINAE约束的)完美转发模板版本void add(T const& val) { _impl.push_back(val); }
void add(T&& val) { _impl.push_back(std::move(val)); }
,它将接受所谓的通用引用(斯科特创造的非标准术语)迈尔斯):
add()
这两种解决方案都是最优的,因为在提供左值时只执行一次复制,并且在提供右值时只执行一次移动。