在函数重载中将右值引用实现为参数 [英] Implementing rvalue references as parameters in function overloads

查看:155
本文介绍了在函数重载中将右值引用实现为参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经问过代码审查和软件工程问题,但是该主题不适合该网站,因此我想在这里希望这不是基于观点的。我是一位老派 C ++开发人员(我停在C ++ 2003中),但是现在我读了几本有关现代C ++ 11/17的书,并且正在重写我的一些库。

I've already asked on code review and software engineering but the topic didn't fit the site, so I'm asking here hoping this is not opinion-based. I am an "old school" C++ developer (I've stopped at C++ 2003) but now I've read a few books on modern C++ 11/17 and I'm rewriting some libraries of mine.

我要做的第一件事是在需要的地方添加move构造函数/赋值运算符(=已经具有析构函数的类+复制构造函数和复制赋值)。基本上我使用的是五个规则

The first thing I've made is adding move constructor/assignment operator where needed ( = classes that already had destructor + copy constructor and copy assignment). Basically I'm using the rule of five.

我的大多数函数都声明为

Most of my functions are declared like

func(const std::string& s);

这是通过引用避免复制的常用方法。顺便说一句,还有新的动作语义,还有一些我在书本/在线中找不到的东西。这段代码:

Which is the common way to pass a reference avoiding a copy. By the way there is also the new move semantic and there's somethig that I wasn't able to find in my books/online. This code:

void fun(std::string& x) {
    x.append(" world"); 
    std::cout << x;
}

int main()
{
    std::string s{"Hello "};
    fun(s);
}

也可以写成:

void fun(std::string&& x) {
    x.append(" world"); 
    std::cout << x;
}

int main()
{
    std::string s{"Hello "};
    fun(std::move(s));
    //or fun("Hello ");
    // or fun(std::string {"Hello" });
}

我的问题是:我何时应该声明接受参数的函数右值引用?

My question is: when should I declare functions that accept a paramenter that is a rvalue reference?

我了解&&语义在构造函数和赋值运算符上,但在函数上不是。在上面的示例(第一个函数)中,我有一个 std :: string& x 不能被称为 fun( H​​ello); 当然是因为我应该将类型替换为 const std :: string& x 。但是现在const不允许我更改字符串!

I understand the usage of && semantic on constructors and assignment operators but not really on functions. In the example above (first function) I have a std::string& x which cannot be called as fun("Hello "); of course because I should delcare the type as const std::string& x. But now the const doesnt allow me to change the string!

是的,我可以使用const类型转换,但是我很少进行类型转换(如果是这种情况,它们是动态转换)。 &&&的力量是避免复制,我不必做类似的事情

Yes, I could use a const cast but I rarely do casts (and if it's the case, they're dynamic casts). The power of the && is that I avoid copies, I don't have to do something like

std::string x = "...";
fun(x); //void fun(std::string& x) {}

我可以使用临时值将被移动。我应该在可能的情况下用右值引用声明函数吗?

and I can assing temporary values that will be moved. Should I declare functions with rvalue references when possible?

我有一个用现代C ++ 17重写的库,并且具有以下功能:

I have a library that I'm rewriting with modern C++ 17 and I have functions like:

//only const-ref
Type1 func(const type2& x);
Type3 function(const type4& x);

我问是否值得将所有这些都重写为

I am asking if it's worth rewriting all of them as

//const-ref AND rvalue reference
Type1 func(const type2& x);
Type3 function(const type4& x);

Type1 func(type2&& x);
Type3 function(type4&& x);

我不想创建太多可能没有用的重载,但是如果我的图书馆的用户想使用移动操作,我应该创建&&参数类型。当然,我不是针对原始类型(int,double,char ...),而是针对容器或类。您的建议是什么?

I don't want to create too many overloads that may be useless but if an user of my library wanted to use the move operation I should create the && param types. Of course I am not doing this for primitive types (int, double, char...) but for containers or classes. What do you suggest?

我不确定后一种情况(两种版本)是否有用。

I am not sure if the latter scenario (with both versions) would be useful or not.

推荐答案

让我对您的问题和示例中的四个方案进行评论。

Let me comment on four scenarios in your question and examples.


  • <$带有传递值的c $ c> std :: string_view 应该替换 const std :: string& 参数,并且只要您可以保证安全使用 std :: string_view 的必要前提条件(生命周期,pointee不变),这是开始对函数签名进行现代化的一个很好的选择。

  • const T& T& (其中 T 不受已知使用场景的模板类型的推导。附加到给定的可修改字符串的 void fun 函数仅对 void fun(std :: string&&)有意义。 code>,如果调用代码在调用后不需要结果。在这种情况下,右值引用签名很好地记录了这种期望,这是可行的方法。但是,根据我的经验,这些情况很少见。

  • const T& T& (同样,没有类型推导)具有未知的使用场景。 std :: vector :: push_back 是一个很好的参考,对于rvalue和lvalue引用都是重载的。与移动构造 T 相比,push_back操作被认为是便宜的,这就是为什么重载有意义的原因。如果假定某个函数比这种移动构造要昂贵得多,则按值传递参数是一种有意义的简化方式(另请参见EMC ++中的第41项)。

  • <$发生类型推导时,c $ c> const T& T& 。在此,请尽可能将通用引用与 std :: forward 一起使用,并且参数不能使用const限定。如果未在函数主体中对其进行修改,请使用 const T&

  • std::string_view with pass-by-value is supposed to replace const std::string& parameters and whenever you can guarantee the necessary preconditions for a safe usage of std::string_view (lifetime, pointee doesn't change), it's a good candidate to start modernizing your function signatures.
  • const T& vs. T&& (where T is not subject to template type deduction) with known usage scenarios. The void fun function that appends to a given, modifiable string, will only makes sense as void fun(std::string&&) if calling code doesn't need the result after the call. In this case, the rvalue-reference signature documents this expectation nicely and is the way to go. But these cases are rather rare in my experience.
  • const T& vs. T&& (again, no type deduction) with unknown usage scenarios. A good reference here is std::vector::push_back, which is overloaded for both rvalue and lvalue references. The push_back operation is assumed to be cheap compared to move-construction a T, that's why the overload makes sense. When a function is assumed to be more expensive than such a move-construction, passing the argument by value is a simplification that can make sense (see also Item 41 in EMC++).
  • const T& vs. T&& when type deduction takes place. Here, use universal references together with std::forward whenever possible and the parameters can't be const qualified. If they aren't modified in the function body, go with const T&.

这篇关于在函数重载中将右值引用实现为参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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