当前位置: 首页 > 知识库问答 >
问题:

左值引用和右值引用之间的重载解析

范志勇
2023-03-14
#include <iostream>

using namespace std;

void func(int (&ref)[6]) { cout << "#1" << endl; }
void func(int * &&ref) { cout << "#2" << endl; }

int main()
{
  int arr[6];
  func(arr); // g++(5.4): ambiguous, clang++(3.8): #2, vc++(19.11): #1

  return 0;
}

这两个函数完全匹配。以下是该标准的引用:

标准转换序列S1是比标准转换序列S2更好的转换序列,如果

...

S1和S2是引用绑定(8.5.3),都不引用未使用ref限定符声明的非静态成员函数的隐式对象参数,S1将右值引用绑定到右值,S2将左值引用绑定。

这不意味着第二个更好吗?

更新:

有一个相关的问题。下面的代码是它的简化版本。

#include <iostream>

using namespace std;

void func(int *&) { cout << "#1" << endl; }
void func(int *&&) { cout << "#2" << endl; }

int main()
{
  int arr[6];
  func(arr);  // g++(5.4) and clang++(3.8): #2, vc++(19.11): ambiguous

  return 0;
}

共有1个答案

宗苗宣
2023-03-14

我认为这取决于特定短语的含义。

这两种转换都是等效的,因为我们排除了左值转换(基本上,数组实际上是指针,所以它不算作转换),所以我们进入您在[over.ics.rank]中指出的下一个分界符:

S1和S2都是引用绑定,都不引用未使用ref限定符声明的非静态成员函数的隐式对象参数,S1将右值引用绑定到右值,S2将左值引用绑定

这种情况适用吗?我们有两个引用绑定:

int arr[6];
int (&a)[6] = arr;  // #1
int *&& b = arr;    // #2

这里,#1绑定了一个左值引用#2属于[dcl.init.ref]:

否则,初始值设定项表达式将隐式转换为“cv1 T1”类型的prvalue。应用临时物化转换,并将引用绑定到结果。

arr隐式转换为类型为int*的prvalue,然后绑定到b

所以现在的问题是,[over.ics.rank]中的限制意味着什么?这可能意味着:

  • 通常绑定到右值的右值引用。这显然是克朗的解释。rvalue引用绑定到从arr的prvalue转换中具体化的临时值
  • 具体来说,参数表达式是绑定到右值引用参数的右值。这显然是gcc的解释,并且由于arr不是右值(它是左值),因此跳过该分界线,并且没有后续的分界线适用

我倾向于支持gcc在这里的实施。否则,短语“将右值引用绑定到右值”的意义是什么?右值引用不能绑定到左值。这是多余的。尽管如此,这种解释的措辞也很尴尬。

就这样,我称之为措辞错误。

 类似资料:
  • 左值(赋值操作符“=”的左侧,通常是一个变量)与右值(赋值操作符“=”的右侧,通常是一个常数、表达式、函数调用)之间的差别可以追溯到 Christopher Strachey (C++的祖先语言CPL之父,指称语义学之父)时代。在C++中,左值可被绑定到非const引用,左值或者右值则可被绑定到const引用。但是却没有什么可以绑定到非const的右值(译注:即右值无法被非const的引用绑定),

  • 这个电话含糊不清吗?MSVC选择右值引用重载。GCC表示这是不明确的。Clang也选择右值引用,但如果它是

  • 如果你从事过C++编程,你会对引用比较熟悉,C++的引用允许你为已经存在的对象创建一个新的名字。对新引用所做的访问和修改操作,都会影响它的原型。 例如: int var=42; int& ref=var; // 创建一个var的引用 ref=99; assert(var==99); // 原型的值被改变了,因为引用被赋值了 目前为止,我们用过的所有引用都是左值引用——对左值的引用。lvalue

  • 本文向大家介绍C++11右值引用和转发型引用教程详解,包括了C++11右值引用和转发型引用教程详解的使用技巧和注意事项,需要的朋友参考一下 右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念。右值引用采用T&&这一语法形式,比传统的引用T&(如今被称作左值引用 lvalue reference)多一个&。 如果把经由T&&这一

  • 大多数人说真相可以让我们感到自由,但是在某些情况下,一个巧妙的谎言也可以让人觉得非常轻松。这个Item就是要编制一个“谎言”。因为我们是在和软件打交道。所以我们避开“谎言”这个词:我们是在编制一种“抽象”的意境。 为了声明一个类型T的右值引用,你写下了T&&。下面的假设看起来合理:你在代码中看到了一个"T&&"时,你看到的就是一个右值引用。但是,它可没有想象中那么简单: void f(Widget

  • 我有以下课程 我在下面的代码中使用 由此产生的行为对我来说是可以理解的。在第一次调用中,构造一个小部件,然后调用移动构造函数,并在临时小部件上调用析构函数。 第二个调用也执行相同的操作,除了调用move赋值运算符而不是move构造函数。离开main方法,析构函数在<code>c<code>上调用。 现在是有趣的部分: 如果我省略了对< code>std::move的调用,第一种情况会停止工作,只导