Const引用VS移动语义

时间:2013-07-14 18:28:48

标签: c++11 const move-semantics

我想知道在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);
    }
};

我是对的吗?如果是这样,假设它可以在任何情况下使用是否安全?如果没有,我想知道哪个。 这将使生活变得更加容易,因为我不必为基本类型实现类专门化,例如,它看起来更清晰。

1 个答案:

答案 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()

这两种解决方案都是最优的,因为在提供左值时只执行一次复制,并且在提供右值时只执行一次移动。