在 RValue 对象上调用 LValue ref 限定成员函数

LValue ref qualified member function being called on an RValue object

我想弄清楚为什么以下代码片段会调用 LValue 转换运算符重载:

#include <iostream>

class Foo
{
public:
    Foo(int i = 0) : i(i) {}

    operator const int& () const &
    {
        std::cout << "lvalue\n";
        return i;
    }
    
    operator int () const &&
    {
        std::cout << "rvalue\n";
        return i;
    }
    
    int i = 0;
};

Foo Fool()
{
    return Foo(5);
}

int main()
{
    const int& i = Fool();
    const int j = Fool();
    
    return 0;
}

当前输出为:

lvalue

rvalue

但是根据我的理解 Fool() returns 一个 rvalue 并且因为 const& 可以绑定到 rvalues 所以不需要构造一个 lvalue Foo.

谁能解释为什么要建造 lvalue?我相信这是悬而未决的 lvalue.

好的,所以这里要注意的是,重载决议只考虑 i 的一个转换函数。它们并不都参与,因此不能使用引用限定词来区分它们。对于绑定引用的情况

[over.match.ref]

Under the conditions specified in [dcl.init.ref], a reference can be bound directly to the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “reference to cv1 T” is the type of the reference being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

  • The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type “lvalue reference to cv2 T2” (when initializing an lvalue reference or an rvalue reference to function) or “cv2 T2” or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where “cv1 T” is reference-compatible with “cv2 T2”, are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type “lvalue reference to cv2 T2” (when initializing an lvalue reference or an rvalue reference to function) or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where T2 is the same type as T or can be converted to type T with a qualification conversion, are also candidate functions.

根据粗体文字,在初始化i时,我们只有个候选者是operator int const&。所以重载决议要么在这里通过,要么完全失败。但它不能 select operator int,因为那个甚至不在考虑之列。它成功是因为 const 限定的左值引用可以绑定到对象参数。

另一方面,用于初始化一个值

[over.match.conv]

Under the conditions specified in [dcl.init], as part of an initialization of an object of non-class type, a conversion function can be invoked to convert an initializer expression of class type to the type of the object being initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

  • The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion are also candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. A call to a conversion function returning “reference to X” is a glvalue of type X, and such a conversion function is therefore considered to yield X for this process of selecting candidate functions.

因此,在初始化 j 时,两个转换函数都作为重载参与,这里引用限定符有所不同。

你在这里确实得到了一个悬垂的引用,这似乎是由于语言中的一个黑暗角落。第一个引用段落中的项目符号可能会被改进,以更好地考虑 const lvlaue 引用的绑定。由于它们也可能绑定到临时对象,因此您的第二个转换运算符理想情况下可以成为更好规则下的候选对象。