const参考VS move语义 [英] Const reference VS move semantics

查看:88
本文介绍了const参考VS move语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道自C ++ 11起在哪些情况下我仍然需要在参数中使用const引用.我不完全了解移动语义,但是我认为这是一个合法的问题. 此问题仅适用于以下情况:const引用替换正在复制的副本,而仅需要读取"该值(例如const成员函数的用法).

I was wondering in which situations I still need to use const references in parameters since C++11. I don't fully understand move semantics but I think this is a legit question. This question is meant for only the situations where a const reference replaces a copy being made while it is only needed to "read" the value (e.g. usage of const member functions).

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

Normally I would write a (member)function like this:

#include <vector>

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

但是我认为可以安全地假设,如果我这样写,并且class T当然实现了移动构造函数,编译器将使用移动语义对其进行优化:

But I'm thinking that it's safe to assume that to compiler would optimize it using move semantics if I write it like this and class T ofcourse implements a move constructor:

#include <vector>

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

我是对的吗?如果是这样,可以安全地假定它可以在任何情况下使用吗?如果没有,我想知道哪个. 这将使工作变得更加轻松,因为除了看起来更整洁外,我不必为基本类型实现类专门化.

Am I right? If so, is it safe to assume it can be used in any situation? If not, I would like to know which. This would make life much easier since I wouldn't have to implement an class specialization for fundamental types for example, besides it looks much cleaner.

推荐答案

您提出的解决方案:

void add(T value) {
     _impl.push_back(value);
}

将需要进行一些调整,因为即使您将右值传递给add()(如果传递左值,则是两个副本),您也总是会执行value的一个副本:因为value是一个左值,当您将其作为参数传递给push_back时,编译器不会自动从中移出.

Would require some adjustment, since this way you do always end up performing one copy of value, even if you pass an rvalue to add() (two copies if you pass an lvalue): since value is an lvalue, the compiler won't automatically move from it when you pass it as an argument to push_back.

相反,您应该这样做:

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

这是更好的方法,但对于模板代码而言仍然不够好,因为您不知道T的价格便宜还是昂贵.如果T是这样的POD:

This is better, but still not sufficiently good for template code, because you do not know if T is cheap or expensive to move. If T is a POD like this:

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

然后移动它不会比复制它更快(实际上,移动它与复制它是同一件事).因为您的通用代码应该避免不必要的操作(那是因为这些操作对于某些类型而言可能很昂贵),所以您无法承受按值获取参数的情况.

Then moving it won't be any faster than copying it (in fact, moving it would be the same thing as copying it). Because your generic code is supposed to avoid unnecessary operations (and that's because those operations may be expensive for some types), you cannot afford taking the parameter by value.

一种可能的解决方案(C ++标准库采用的解决方案)是提供add()的两个重载,一个重载左值引用,另一个重载右值引用:

One possible solution (the one adopted by the C++ standard library) is to provide two overloads of add(), one taking an lvalue reference and one taking an rvalue reference:

void add(T const& val) { _impl.push_back(val); }
void add(T&& val) { _impl.push_back(std::move(val)); }

另一种可能性是提供add()的(可能受到SFINAE约束的)完美转发模板版本,该版本将接受所谓的通用引用(由Scott Meyers创造的非标准术语) :

Another possibility is to provide a (possibly SFINAE-constrained) perfect-forwarding template version of add() that would accept a so-called universal reference (non-standard term coined by Scott Meyers):

template<typename U>
void add(U&& val) { _impl.push_back(std::forward<U>(val)); }

从提供lvalues时仅执行一次复制,提供rvalues时仅执行一次移动的意义上讲,这两种解决方案都是最佳的.

Both these solutions are optimal in the sense that only one copy is performed when lvalues are provided, and only one move is performed when rvalues are provided.

这篇关于const参考VS move语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆