Tim*_*Tim 33 const move-semantics c++11
我想知道在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);
}
};
Run Code Online (Sandbox Code Playgroud)
但是我认为可以安全地假设编译器会使用移动语义来优化它,如果我这样写它并且class Tofcourse实现了一个移动构造函数:
#include <vector>
template<class T>
class Vector {
std::vector<T> _impl;
public:
void add(T value) {
_impl.push_back(value);
}
};
Run Code Online (Sandbox Code Playgroud)
我对吗?如果是这样,假设它可以在任何情况下使用是否安全?如果没有,我想知道哪个.这将使生活变得更加容易,因为我不必为基本类型实现类专门化,例如,它看起来更清晰.
And*_*owl 31
你提出的解决方案:
void add(T value) {
_impl.push_back(value);
}
Run Code Online (Sandbox Code Playgroud)
需要进行一些调整,因为这样你总是会执行一个副本value,即使你传递了一个rvalue add()(如果你传递一个左值value也是两个副本):因为是左值,编译器不会自动从它移动你把它作为参数传递给push_back.
相反,你应该这样做:
void add(T value) {
_impl.push_back(std::move(value));
// ^^^^^^^^^
}
Run Code Online (Sandbox Code Playgroud)
这样做更好,但对模板代码仍然不够好,因为你不知道T移动是否便宜或昂贵.如果T是这样的POD:
struct X
{
double x;
int i;
char arr[255];
};
Run Code Online (Sandbox Code Playgroud)
然后移动它将不会比复制它更快(事实上,移动它将与复制它相同).因为您的通用代码应该避免不必要的操作(这是因为某些类型的操作可能会很昂贵),所以您无法承担按值获取参数的费用.
一种可能的解决方案(C++标准库采用的解决方案)是提供两个重载add(),一个采用左值引用,另一个采用右值引用:
void add(T const& val) { _impl.push_back(val); }
void add(T&& val) { _impl.push_back(std::move(val)); }
Run Code Online (Sandbox Code Playgroud)
另一种可能性是提供一种(可能是SFINAE约束的)完美转发模板版本add(),它将接受所谓的通用参考(Scott Meyers创造的非标准术语):
template<typename U>
void add(U&& val) { _impl.push_back(std::forward<U>(val)); }
Run Code Online (Sandbox Code Playgroud)
这两种解决方案都是最优的,因为在提供左值时只执行一次复制,并且在提供右值时仅执行一次移动.