根据 FCD 的说法,GCC 的做法是错误的。 FCD 表示8.5.3
关于引用绑定
- 如果引用是左值引用并且初始化表达式是 [左值/类类型] ...
- 否则,该引用应为对非易失性 const 类型的左值引用(即 cv1 应为 const),或者该引用应为右值引用,并且初始值设定项表达式应为右值或具有函数类型。
您致电的情况std::string &&
它们都不匹配,因为初始化器是一个lvalue。它没有到达创建临时右值的位置,因为该顶级项目符号已经需要一个右值。
现在,重载解析不直接使用引用绑定来查看是否存在隐式转换序列。相反,它说在13.3.3.1.4/2
当引用类型的参数未直接绑定到参数表达式时,转换序列是根据 13.3.3.1 将参数表达式转换为引用的基础类型所需的序列。
因此,重载决策会找出一个获胜者,即使该获胜者实际上可能无法绑定到该参数。例如:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&);
void f(B);
int main() { A a; f(a.bits); }
参考绑定位于8.5
禁止位域绑定到左值引用。但重载解析表明转换序列是转换为的序列int
,因此即使稍后进行调用时,调用格式不正确,也会成功。因此我的位域示例格式不正确。如果要选择的话B
版本,它会成功,但需要用户定义的转换。
However,该规则存在两个例外。这些都是
除了隐式对象参数(参见 13.3.1)外,如果需要将非 const 的左值引用绑定到右值或将右值引用绑定到左值,则无法形成标准转换序列。
因此,以下调用是有效的:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }
因此,你的例子调用const T&
version
void f(const std::string &);
void f(std::string &&); // would bind to lvalue!
void g(const char * arg) { f(arg); }
但是,如果你说f(arg + 0)
,您创建一个右值,因此第二个函数是可行的。