为什么 C++ 在函数调用时优先使用右值引用而不是常量引用?[复制]

Why does C++ give preference to rvalue reference over const reference while function call? [duplicate]

提问人:simplekind 提问时间:6/30/2021 最后编辑:user438383simplekind 更新时间:5/25/2022 访问量:341

问:

所以我用 C++ 11 写了一段代码

#include <iostream>
using namespace std;

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

代码的输出很吸引人,因为它是“rval refernce” 但是,如果我重写代码,只是删除一个函数定义:

#include <iostream>
using namespace std;

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

我得到的输出是“const ref”

编辑: 如果我再次将代码重写为

#include <iostream>
using namespace std;

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int&& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

还是它打印的“rval ref”,请解释一下逻辑

有人可以解释为什么C++在传递 ravlue 作为参数时优先考虑 && 而不是 const 吗?## 标题##

C++ 按引用传递 rvalue-reference const-reference 传递

评论

7赞 François Andrieux 6/30/2021
文本 like 是右值,因此首选右值引用重载。9
4赞 HolyBlackCat 6/30/2021
没有什么比你看到的更多了。 是一个右值,因此具有 rvalue ref 参数的重载具有优先权。但是 const lvalue refs 也可以绑定到右值。9
1赞 NathanOliver 6/30/2021
为什么右值引用的值优先级不高于左值引用?
1赞 Richard Critten 6/30/2021
整数文本是 prvalue - en.cppreference.com/w/cpp/language/value_category 它是右值,因此更喜欢绑定到右值引用
2赞 Richard Critten 6/30/2021
上述链接的确切引用是rvalue:......当用作函数参数时,当函数的两个重载可用时,一个采用右值引用参数,另一个采用常量参数的左值引用,则右值绑定到右值引用重载...”

答:

1赞 user12002570 5/24/2022 #1

让我们根据具体情况看看发生了什么:

案例 1

在这里,我们考虑:

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9); //chooses print(int&&) version  
}

这种情况 1 的行为可以从 over.ics.rank 中理解,它指出:

3.2 如果满足以下条件,则标准转换序列 S1 是比标准转换序列 S2 更好的转换序列

S1并且是引用绑定 ([dcl.init.ref]),并且两者都不引用没有 ref 限定符声明的非静态成员函数的隐式对象参数,并且 S1 将右值引用绑定到右值,S2 绑定左值引用S2

(强调我的)

这意味着在本例 1 中,带有 的版本优先于 .int&&const int&


案例 2

在这里,我们考虑:

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   //chooses the only viable and available option print(const int&)
}

在情况 2 中,由于允许const 对象的左值引用绑定到右值,因此提供的是可行的,并且是唯一可用的,因此被选中。print(const int&)

案例 3

在这里,我们考虑:

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int&& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

现在,它选择第一个版本,因为这是一个 prvalue 而不是 prvalue。另请注意,对于 prvalue,它将在进行任何进一步分析之前被剥离,因此将选择第一个版本。这是在 expr#6 中指定的:print(int&&)9intconst intconst intconstprint(int&&)

如果 prvalue 最初具有 类型 ,其中 是 cv 非限定的非类、非数组类型,则在进行任何进一步分析之前,表达式的类型将调整为 Tcv TT

这意味着对于类类型 prvalues,将选择函数的第二个版本,这与如下所示的内置类型不同。constprintprint(const C&&)int

struct C 
{
    C()
    {
        std::cout<<"default ctor"<<std::endl;
    }
};
void print (C &&a)
{
    cout<<"rval ref";
}

void print (const C&& a)
{
    cout<<"const ref";
}
const C func()
{
    const C temp;
    return temp;
}
int main()
{
    print(func()); //prints const ref  
}

上述程序对类类型的输出为:C

default ctor
const ref

从上面的例子中可以明显看出,对于类类型,将选择第二个版本的print with。const C&&

评论

0赞 Peter - Reinstate Monica 5/24/2022
我认为 OP 很困惑为什么文字不绑定到 const 右值引用——我的意思是,没有什么比文字更常量了,是吗?9
0赞 user12002570 5/24/2022
@Peter-ReinstateMonica:这是因为对于像 prvalue 这样的内置类型,prvalue 被调整为意味着它被剥离了。因此,即使有一个 prvalue,它也会被剥离,因此将选择第一个版本。但是这种对 const 的剥离不会发生在类类型中,因此如果我们要传递类类型 const prvalue,则将选择第二个版本。我在回答中对此进行了更详细的解释。查看我更新的答案(尤其是案例 3)。intconst intintconstconst intconstprintint&&print