c++11 - Const 引用 VS move 语义

标签 c++11 constants move-semantics

我想知道自 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/

相关文章:

c++ - std::list remove_if 删除节点?

C++ 什么时候标准库容器中的 typedef 不是您所期望的?

c++ - 我怎样才能创建我的单例常量实例?

arrays - Julia 初始化数组/向量不是零而是随机

c++ - 不合理删除的 move 构造函数

c++ - 将指针传递给导致第二次调用使程序崩溃的函数

c++ - 从工厂函数返回右值引用时堆/内存损坏

C++/Qt 编码风格——#define 应该放在哪里

c++ - 将 `std::move(*this)` 放入从 `this->some_method` 创建的对象中是否安全?

c++ - 为什么std::move复制右值或const左值函数参数的内容?