简体   繁体   English

(右值引用)VS(const左值引用)作为C ++ 11中的函数参数

[英](rvalue reference) VS (const lvalue reference) as function parameters in C++11

Can someone explain when rvalue references are preferred over const lvalue references when working as function parameters? 当作为函数参数工作时,有人能解释何时rvalue引用优于const lvalue引用?

Background : I was trying to pass a const pointer into a function. 背景 :我试图将一个const指针传递给一个函数。 Since I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return), I have two choices: the parameter could either be declared as: 由于我必须考虑传入本地指针并传入临时的情况(例如从函数调用返回),我有两个选择:参数可以声明为:

void foo(T const* const&); //const lvalue ref to const ptr

or 要么

void foo(T const* &&); //rvalue ref to const ptr

But this rvalue reference cannot be bound to a local variable (which is of lvalue type. But I did remember Scott Meyers coined the term "universal reference" to refer to rvalue reference. This confuses me more.) So my question is, since the first declaration could deal with both cases, when would the second one using rvalue reference be preferred? 但是这个rvalue引用不能绑定到局部变量(它是左值类型。但我确实记得Scott Meyers创造了术语“通用引用”来引用右值引用。这让我更加困惑。)所以我的问题是,因为第一个声明可以处理这两种情况,第二个使用右值引用的时候是首选吗?

Note : In the first approach, the other forms 注意 :在第一种方法中,其他形式

void foo(const const T* &); 
void foo(const T* const&); 

didn't work. 没用。 I guess the reason is that in the latter two I was not consistent in the place where the const qualifiers are put into (please correct me if I'm wrong). 我想原因是在后两者中我在const限定符的位置上并不一致(如果我错了,请纠正我)。

It is very rarely a good idea to pass a pointer by const & : at best it takes the same overhead, at worst it causes extremely complex pointer reseating logic to surprise readers of your code. 通过const &传递指针几乎不是一个好主意:最好它需要相同的开销,最坏的情况是它会导致极其复杂的指针重置逻辑,让你的代码读者感到惊讶。

Take pointers by value -- T const* -- and things are more sane. 按值来指点 - T const* - 事情更加清醒。

References to non-pointer values make more sense. 对非指针值的引用更有意义。

Universal references is a technique using rvalue and lvalue references in a type deduction context. 通用引用是一种在类型推导上下文中使用rvalue和左值引用的技术。 It basically only applies when you have a type T&& being deduced from an expression -- in that context T can be X , X& or X const& (or other cv variants). 它基本上只适用于从表达式中推导出类型T&&情况 - 在该上下文中T可以是XX&X const& (或其他cv变体)。

If T is X& or X const& , the rvalue reference to the lvalue reference collapses into a lvalue reference. 如果TX&X const& ,则左值引用的右值引用会折叠为左值引用。 It is an example of the standard committee being clever, and it allows auto&&x= based universal reference variables, and perfect forwarding code to be easy to write. 这是标准委员会聪明的一个例子,它允许基于auto&&x=的通用引用变量,并且完美的转发代码易于编写。

You do not want to differentiate between a temporary pointer and an lvalue pointer. 您不希望区分临时指针和左值指针。 That looks to me like something that's bound to fail rather sooner than later. 在我看来,这样的事情必然会比以后更早失败。

Universal Refs only apply in template functions like Universal Refs仅适用于模板功能

 template<class T> void foo(T && fwdref);

Note that a "universal ref" is not the same as an rvalue ref. 需要注意的是“通用裁判”是一样的右值裁判。

Remark: I wrote this answer under the assumption that T in your question represents some actual data type -- I've chosen int in my examples below. 备注:我在假设你的问题中的T代表一些实际数据类型的情况下写了这个答案 - 我在下面的例子中选择了int

Background: I was trying to pass a const pointer into a function. 背景:我试图将一个const指针传递给一个函数。 [...] I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return) [...]我必须考虑传入本地指针的情况,并在其中传入临时(例如从函数调用返回)

You didn't say what you mean by "const pointer". 你没有说“const指针”是什么意思。 I will first assume you mean a pointer that is itself constant (ie the address to which it points cannot be changed). 我首先假设你指的是一个本身不变的指针(即它指向的地址不能改变)。

According to your description, there are basically two ways you get such a pointer: 根据您的描述,基本上有两种方法可以获得这样的指针:

// Case 1 (what you call a local pointer -- this should be inside some
//         function body):
int *const p = 0;

// Case 2, a function that returns a pointer; this is your rvalue case
// in contexts where f() is called and its return value used as a temporary:
int *f()
{ return 0; }

// Note: The temporary returned by this function isn't, strictly speaking,
//       constant. It could be modified as long as it is alive. But from
//       your description I take it that you have no intentions of doing so
//       and/or regard temporaries as generally constant.

Now you can define a function that accepts these two cases as follows: 现在您可以定义一个接受这两种情况的函数,如下所示:

void g(int *const &arg)
{ }

You can apply this as g(p); 您可以将其应用为g(p); to a constant, local pointer such as p defined earlier, as well as to a temporary g(f()); 到一个常量的本地指针,如前面定义的p ,以及一个临时的g(f()); . (You could, thirdly, apply it to a non-const local pointer as well, because going from non-const lvalue to const lvalue is never a problem.) (你可以,第三,也可以将它应用于非const局部指针,因为从非const左值到const左值从来都不是问题。)

This function g has a function argument arg which is defined as a constant, lvalue reference to an int -pointer. 这个函数g有一个函数参数arg ,它被定义为对int -pointer的常量lvalue引用。 It can bind to a constant (or indeed non-constant) local pointer (such as p ) as well as a temporary, because constant lvalue references, unlike non-constant lvalue references, can do that. 它可以绑定到常量(或实际上非常量)本地指针(例如p )以及临时值,因为与非常量左值引用不同,常量左值引用可以做到这一点。

Remark: It's not clear to me why, in this case, you need the function argument to be a reference at all. 备注:我不清楚为什么在这种情况下,你需要函数参数作为参考。 You could simply declare void g(int *const arg) (no ampersand) and do without a reference. 你可以简单地声明void g(int *const arg) (没有&符号)并且没有引用。 Reasons include a) You cannot modify it anyway; 原因包括a)无论如何你不能修改它; b) In all real-world implementations, the reference will take just as much (or as little) space as the pointer itself, so there is no point in avoiding a copy. b)在所有实际实现中,引用将占用与指针本身一样多(或少)的空间,因此避免复制没有意义。

Anyway. 无论如何。 If you want you can also define a second version of g specifically for rvalue references: 如果需要,还可以为rvalue引用定义第二个g版本:

void g(int *&& arg)
{ }

This can only be applied to the temporary, not to the local pointer, because the function argument is defined as an rvalue reference, which can bind to temporaries, but not to lvalues. 只能应用于临时而不是本地指针,因为函数参数被定义为右值引用,它可以绑定到临时值,但不能绑定到左值。

However, if by "const pointer" you actually mean a pointer-to-const, ie a pointer that can be changed to different addresses, but does not have the power to modify the value stored at those addresses, the declarations are a bit different. 但是,如果通过“const指针”实际上意味着指向const的指针,即可以更改为不同地址的指针,但无法修改存储在这些地址中的值,则声明会有所不同。 The keyword const must then be put before the asterisk, and for better clarity best before the type specifier int : 然后必须将关键字const放在星号之前,为了更清晰,最好在类型说明符int之前:

// Declare local pointer-to-const:
const int *p = 0;

// Function that returns a pointer-to-const:
const int *f()
{ return 0; }

A function that can accept these two would then be declared as: 然后可以将接受这两个函数的函数声明为:

void g(const int *const &arg)
{ }

The first const means we are talking about pointers-to-const, and the second const ensures we have a constant lvalue-reference, which can bind to both rvalues and lvalues. 第一个const表示我们正在讨论指向const的指针,第二个const我们有一个常量左值引用,它可以绑定到rvalues和lvalues。 Note that this function can not modify what arg points to, because arg is declared as a constant lvalue reference. 请注意,此函数无法修改arg指向的内容,因为arg被声明为常量左值引用。 In the case where arg binds to the temporary, that is probably what we want anyway (as stated above). arg与临时绑定的情况下,这可能是我们想要的(如上所述)。 But in the case where the function is called as g(p); 但是在函数被称为g(p); , we might actually want to modify the local pointer p from within g . ,我们可能实际上想要从g修改本地指针p If you want g to have this power, you need to define two versions of it: 如果你想要g具有这种能力,你需要定义它的两个版本:

void g(const int *&& arg)
{ /* Can bind to temporaries, but not modify them. */ }

void g(const int *& arg)
{ /* Can bind to local variables and modify what they point at */ }

Remark 1: Your original declaration const int *const &const is useless (and not even accepted by GCC). 备注1:你的原始声明const int *const &const是无用的(甚至不被GCC接受)。 It would mean a "constant reference to a constant pointer to constant int", but since a reference to a constant pointer is implicitly itself a const-reference, the final const is superfluous (and not provided for by the Standard). 它意味着“对常量指针的常量指针的常量引用”,但由于对常量指针的引用本身就是一个const引用,因此最终的const是多余的(标准没有提供)。

Remark 2: Universal references are not the same as rvalue references. 备注2:通用引用与右值引用不同。 Universal references are declared as T &&arg where T is a template parameter. 通用引用声明为T &&arg ,其中T是模板参数。 Depending on what T refers to in each instantiation of the template, this may be an lvalue reference or an rvalue reference -- hence its "universal" character. 根据T在模板的每个实例化中引用的内容,这可以是左值引用或右值引用 - 因此它是“通用”字符。 This has nothing to do with your use case, anyway, though, since you are dealing with pointers T * here (even if we assume that T is a template parameter). 无论如何,这与你的用例无关,因为你在这里处理指针T * (即使我们假设T是模板参数)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C ++ 11:抽象const,volatile,左值引用和rvalue引用限定成员函数指针? - C++11: Abstracting over const, volatile, lvalue reference, and rvalue reference qualified member function pointers? C ++ 11右值引用vs const引用 - C++11 rvalue reference vs const reference C++11 - 为什么编译器不优化对 const 左值引用绑定的右值引用? - C++11 - Why compiler does not optimize rvalue reference to const lvalue reference binding? C ++ 11:为什么将右值引用参数隐式转换为左值 - C++11: Why rvalue reference parameter implicitly converted to lvalue 当参数类型为C ++ 11中的常量左值引用与非常量左值引用时,函数模板类型推导的工作方式 - How function template type deduction works when parameter type is const lvalue reference vs non-const lvalue refernce in c++11 C ++,在函数中使用const左值和右值引用 - C++, take const lvalue and rvalue reference in a function rvalue或lvalue(const)引用参数 - rvalue or lvalue (const) reference parameter clang ++ with c ++ 11 flag显示rvalue引用无法绑定到左值错误 - clang++ with c++11 flag shows rvalue reference cannot bind to lvalue error 左值参考的右值参考与左值/右值的右值参考 - rvalue refrence of lvalue reference vs rvalue reference of lvalue/rvalue C ++ 11右值引用寻址 - C++11 rvalue reference addressing
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM