C 11 Const参考VS移动语义

我想知道在哪些情况下,我仍然需要在C 11中使用参数中的const引用.我不完全理解移动语义,但我认为这是一个合法的问题.
这个问题仅适用于在const引用替换正在进行的副本的情况下,而仅需要“读取”值(例如,常量成员函数的使用).

通常我会写一个这样的(成员)函数:

#include <vector>

template<class T>
class Vector {
    std::vector<T> _impl;
public:
    void add(const T& value) {
         _impl.push_back(value);
    }
};

但是我认为假设编译器会使用移动语义来优化它,如果我这样写,并且类T实现一个移动构造函数是安全的:

#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);
}

需要一些调整,因为这样做总是最终执行一个值的副本,即使你传递一个值来添加()(两个副本,如果你传递一个左值):由于值是一个左值,编译器将不会当您将其作为参数传递给push_back时,它将自动移动.

相反,你应该这样做:

void add(T value) {
     _impl.push_back(std::move(value));
//                   ^^^^^^^^^
}

这是更好的,但对于模板代码仍然不够好,因为你不知道T是便宜还是昂贵的移动.如果T是这样的POD:

struct X
{
    double x;
    int i;
    char arr[255];
};

然后移动它将不会比复制更快(实际上,移动它将与复制相同).因为你的通用代码应该避免不必要的操作(这是因为这些操作对某些类型的操作可能是昂贵的),你无法承担使用参数的值.

一个可能的解决方案(C标准库采用的)是提供两个add()的重载,一个取值为lvalue的引用,另一个为rvalue引用:

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)); }

这两种解决方案都是最佳的,因为在提供l值时只执行一个副本,并且只提供r值时只执行一个移动.

转载注明原文:C 11 Const参考VS移动语义 - 代码日志