在 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 引用的绑定。由于它们也可能绑定到临时对象,因此您的第二个转换运算符理想情况下可以成为更好规则下的候选对象。
我想弄清楚为什么以下代码片段会调用 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 引用的绑定。由于它们也可能绑定到临时对象,因此您的第二个转换运算符理想情况下可以成为更好规则下的候选对象。