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

时间:2014-01-03 05:37:37

标签: c++ c++11 rvalue-reference

有人可以解释当作为函数参数工作时,rvalue引用优先于const左值引用吗?

背景: 我试图将const指针传递给函数。由于我必须考虑传入本地指针并传入临时的情况(例如从函数调用返回),我有两个选择:参数可以声明为:

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

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

但是这个右值引用不能绑定到局部变量(它是左值类型。但我确实记得Scott Meyers创造了术语“通用引用”来引用右值引用。这让我更加困惑。)所以我的问题是,因为第一个声明可以处理这两种情况,第二个使用右值引用的时候是首选吗?

注意:在第一种方法中,其他形式

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

没用。我想原因是在后两者中我在const限定符的位置上并不一致(如果我错了,请纠正我)。

3 个答案:

答案 0 :(得分:8)

通过const &传递指针几乎不是一个好主意:最多只需要相同的开销,最坏的情况是它会导致非常复杂的指针重置逻辑,让您的代码读者感到惊讶。

按值计算指针 - T const* - 事情更加清晰。

对非指针值的引用更有意义。

通用引用是一种在类型推导上下文中使用rvalue和左值引用的技术。它基本上仅适用于从表达式中推导出类型T&&的情况 - 在该上下文中T可以是XX&X const&(或其他cv变种)。

如果TX&X const&,则左值引用的右值引用会折叠为左值引用。这是标准委员会聪明的一个例子,它允许基于auto&&x=的通用参考变量,并且完美的转发代码易于编写。

答案 1 :(得分:2)

您不希望区分临时指针和左值指针。在我看来,这种事情必然会在不久之后失败。

Universal Refs仅适用于

等模板功能
 template<class T> void foo(T && fwdref);

请注意,“通用引用”与rvalue引用相同。

答案 2 :(得分:2)

备注:我在假设您的问题中的T代表某些实际数据类型的情况下写了这个答案 - 我在下面的示例中选择了int。< / p>

  

背景:我试图将const指针传递给函数。 [...]我必须考虑传入本地指针并传入临时的情况(例如从函数调用返回)

你没有说“const指针”是什么意思。我首先假设你的意思是指针本身是不变的(即它指向的地址不能改变)。

根据您的描述,基本上有两种方法可以获得这样的指针:

// 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.

现在您可以定义一个接受这两种情况的函数,如下所示:

void g(int *const &arg)
{ }

您可以将此g(p);应用于常规的本地指针,例如前面定义的p,以及临时g(f());。 (你可以,第三,也可以将它应用于非const局部指针,因为从非const左值到const左值从来都不是问题。)

此函数g有一个函数参数arg,它被定义为对int指针的常量左值引用。它可以绑定到常量(或实际上非常量)本地指针(例如p)以及临时指针,因为与非常量左值引用不同,常量左值引用可以这样做。

备注:我不清楚为什么在这种情况下,你需要函数参数作为参考。你可以简单地声明void g(int *const arg)(没有&符号)并且没有引用。原因包括a)无论如何你不能修改它; b)在所有实际实现中,引用将占用与指针本身一样多(或少)的空间,因此避免复制是没有意义的。

反正。如果您愿意,还可以为rvalue引用定义第二版g

void g(int *&& arg)
{ }

这只能将 应用于临时而不是本地指针,因为函数参数被定义为右值引用,它可以绑定到临时值,但不能绑定到左值。

但是,如果通过“const指针”实际上意味着指向const的指针,即可以更改为不同地址的指针,但无法修改存储在这些地址中的值,则声明为有点不同。然后必须将关键字const放在星号之前,为了更清晰,最好在类型说明符int之前:

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

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

然后可以将接受这两个的函数声明为:

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

第一个const表示我们正在讨论指向const的指针,而第二个const确保我们有一个常量左值引用,它可以绑定到rvalues和lvalues。请注意,此函数无法修改arg指向的内容,因为arg被声明为常量左值引用。在arg绑定临时的情况下,这可能是我们想要的(如上所述)。但是在函数被称为g(p);的情况下,我们实际上可能希望从p内修改本地指针g。如果您希望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 */ }

备注1:您的原始声明const int *const &const无效(GCC甚至不接受)。它将意味着“对常量指针的常量指针的常量引用”,但由于对常量指针的引用本身就是一个const引用,因此最终的const是多余的(标准没有提供)

备注2:通用引用与右值引用不同。通用引用声明为T &&arg,其中T是模板参数。根据模板的每个实例化中T引用的内容,这可能是左值引用或右值引用 - 因此它是“通用”字符。无论如何,这与您的用例无关,因为您在这里处理指针T *(即使我们假设T是模板参数)。