C++ 当需要用户定义的转换时,左值和右值引用参数之间的重载解析

C++ 当需要用户定义的转换时,左值和右值引用参数之间的重载解析,c++,overload-resolution,C++,Overload Resolution,考虑以下代码: #include <cstdio> struct S { S(int) {} }; void f(const S&) { std::puts("const S&"); } void f(S&&) { std::puts("S&&"); } int main() { f(42); } #包括 结构{ S(int){} }; void f(常数S&){std::put(“常数S&”)} void f(S&&

考虑以下代码:

#include <cstdio>
struct S {
    S(int) {}
};
void f(const S&) { std::puts("const S&"); }
void f(S&&) { std::puts("S&&"); }
int main() {
    f(42);
}
#包括
结构{
S(int){}
};
void f(常数S&){std::put(“常数S&”)}
void f(S&&{std::put(“S&&”)}
int main(){
f(42);
}
显然,应该调用
S&&
重载,但我对标准的理解是,实际上没有规则要求这种情况

当然,您会说对
f
的调用必须初始化一个临时
S
对象,重载解析更倾向于将右值引用绑定到右值,而不是绑定左值引用。然而,这是标准在[over.ics.rank]/(3.2)中实际说明的:

标准转换顺序
S1
比标准转换顺序
S2
更好

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

因此,这个关于左值和右值引用绑定的分界线只适用于标准转换序列。但是,如果给定
int
参数,则需要用户定义的转换序列来调用
f
的重载。对用户定义的转换序列进行排序的规则为(3.3)

如果用户定义的转换序列
U1
包含相同的用户定义转换函数或构造函数,或者在聚合初始化中初始化相同的类,并且在任何一种情况下都是
U1
优于
U2
的第二个标准转换序列

显然,两个转换序列(to
const S&
S&&
)调用同一个构造函数,因此如果第二个标准转换优于第一个,则第二个可能优于第一个。但是,[over.ics.ref]/2说:

当引用类型的参数未直接绑定到参数表达式时,根据16.3.3.1,转换序列是将参数表达式转换为引用类型所需的序列。从概念上讲,此转换序列对应于使用参数表达式初始化引用类型的临时副本。顶级简历资格的任何差异均包含在初始化中,不构成转换

根据我的阅读,
int
const S&
的隐式转换序列与
int
const S
的用户定义转换序列相同,这只是一个用户定义的转换,第二个标准转换是身份转换;类似地,
int
S&
的隐式转换序列与
int
S
的用户定义转换序列相同,这也是单个用户定义转换,第二个标准转换是身份转换


因此,第二个重载的“明显”选择没有文本基础。。。还是有一个我错过了?

Hm。。。绑定到
常量&
不需要额外的限定转换吗?(如果你有一个
常量&&
重载,也一样。)我不认为这个限定是“顶级的”。@KerrekSB段落说:“转换序列是将参数表达式转换为引用类型所需的序列”。因此,无论将
int
转换为
const S
(此处,cv限定为顶级)需要什么用户定义的转换序列,都需要完全相同的用户定义转换序列(具有相同的重载解析含义)来将
int
转换为
const S&
,因此不需要,没有额外的资格转换。