我想知道自 C++11 以来在哪些情况下我仍然需要在参数中使用 const 引用。我不完全理解 move 语义,但我认为这是一个合法的问题。 这个问题仅适用于 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
当然实现了 move 构造函数,那么可以安全地假设编译器会使用 move 语义来优化它:
#include <vector>
template<class T>
class Vector {
std::vector<T> _impl;
public:
void add(T value) {
_impl.push_back(value);
}
};
我说得对吗?如果是这样,可以安全地假设它可以在任何情况下使用吗?如果没有,我想知道是哪一个。 这将使生活变得更加容易,因为我不必为基本类型实现类专门化,而且它看起来更干净。
最佳答案
您提出的解决方案:
void add(T value) {
_impl.push_back(value);
}
需要进行一些调整,因为这样您总是会执行 value
的一份副本,即使您将右值传递给 add()
(如果您传递一个左值):由于 value
是一个左值,因此当您将其作为参数传递给 push_back
时,编译器不会自动移出它。
相反,您应该这样做:
void add(T value) {
_impl.push_back(std::move(value));
// ^^^^^^^^^
}
这更好,但对于模板代码来说仍然不够好,因为您不知道 T
move 起来便宜还是昂贵。如果 T
是这样的 POD:
struct X
{
double x;
int i;
char arr[255];
};
那么 move 它不会比复制它快(事实上, move 它与复制它是一样的)。因为您的通用代码应该避免不必要的操作(这是因为这些操作对于某些类型来说可能会很昂贵),所以您不能按值获取参数。
一种可能的解决方案(C++ 标准库采用的解决方案)是提供两种 add()
重载,一种采用左值引用,另一种采用右值引用:
void add(T const& val) { _impl.push_back(val); }
void add(T&& val) { _impl.push_back(std::move(val)); }
另一种可能性是提供一个(可能受 SFINAE 约束的)完美转发模板版本的 add()
,它接受所谓的通用引用(非Scott Meyers 创造的标准术语):
template<typename U>
void add(U&& val) { _impl.push_back(std::forward<U>(val)); }
这两种解决方案都是最佳的,因为在提供左值时仅执行一次复制,并且在提供右值时仅执行一次 move 。
关于c++11 - Const 引用 VS move 语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17642357/