在函数重载中将右值引用实现为参数 [英] Implementing rvalue references as parameters in function overloads
问题描述
我已经问过代码审查和软件工程问题,但是该主题不适合该网站,因此我想在这里希望这不是基于观点的。我是一位老派 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( Hello);
当然是因为我应该将类型替换为 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 replaceconst std::string&
parameters and whenever you can guarantee the necessary preconditions for a safe usage ofstd::string_view
(lifetime, pointee doesn't change), it's a good candidate to start modernizing your function signatures.const T&
vs.T&&
(whereT
is not subject to template type deduction) with known usage scenarios. Thevoid fun
function that appends to a given, modifiable string, will only makes sense asvoid 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 isstd::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 aT
, 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 withstd::forward
whenever possible and the parameters can't be const qualified. If they aren't modified in the function body, go withconst T&
.
这篇关于在函数重载中将右值引用实现为参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!