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

Posted

技术标签:

【中文标题】在 RValue 对象上调用 LValue ref 限定成员函数【英文标题】:LValue ref qualified member function being called on an RValue object 【发布时间】:2020-02-27 06:12:47 【问题描述】:

我试图弄清楚为什么以下 sn-p 调用 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;

目前的输出是:

左值

右值

但据我了解,Fool() 返回一个rvalue,并且由于const&amp; 可以绑定到rvalues,因此无需构造lvalue Foo

谁能解释为什么要构造lvalue?我相信这是一个悬空的lvalue

【问题讨论】:

你的编译器是什么?这对我来说就像一个错误 @StoryTeller-UnslanderMonica 我在 clang 和 msvc 上试过这个,都给出了相同的结果。 转换运算符比较特殊,我还在努力理解,详细解释见***.com/questions/54161013/…(我还是没搞懂) 我相信标准不提供任何保证,即通过方法返回的临时实例成员的引用在绑定到 const ref 后仍然有效。 IE。那里可能发生崩溃 @JVApen 我深入研究了这个问题,现在我只能说我和你在同一条船上。使用简单的get() 函数而不是隐式转换运算符似乎可以解决这个问题。 【参考方案1】:

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

[over.match.ref]

在[dcl.init.ref]中指定的条件下,引用可以是 直接绑定到将转换函数应用于 初始化表达式。过载分辨率用于选择 要调用的转换函数。假设“引用 cv1 T” 是被初始化的引用的类型,“cv S”是类型 初始值设定项表达式,具有 S 类类型,候选 功能选择如下:

考虑了 S 及其基类的转换函数。 那些不隐藏在 S 中的非显式转换函数 并产生类型“对 cv2 T2 的左值引用”(在初始化 左值引用或函数的右值引用)或“cv2 T2”或 “对 cv2 T2 的右值引用”(初始化右值引用或 对函数的左值引用),其中“cv1 T”是 与“cv2 T2”的引用兼容,是候选函数。 为 直接初始化,那些显式转换函数 不隐藏在 S 中并产生类型“对 cv2 T2 的左值引用”(当 初始化对函数的左值引用或右值引用) 或“对 cv2 T2 的右值引用”(初始化右值引用时 或对函数的左值引用),其中 T2 与 T 的类型相同或 可以通过限定转换转换为 T 类型,也是 候选函数。

根据粗体字,初始化i时,我们的唯一候选者是operator int const&amp;。所以重载决议可以在这里通过,或者完全失败。但它不能选择operator int,因为它甚至不在考虑之中。它成功了,因为 const 限定的左值引用可以绑定到对象参数。

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

[over.match.conv]

在 [dcl.init] 中指定的条件下,作为 非类类型对象的初始化,转换函数 可以调用将类类型的初始化表达式转换为 正在初始化的对象的类型。使用重载分辨率 选择要调用的转换函数。假设“cv1 T” 是被初始化对象的类型,“cv S”是 初始化表达式,带有 S 类类型,候选 功能选择如下:

考虑了 S 及其基类的转换函数。那些不隐藏在 S 中的非显式转换函数 并产生类型 T 或可以通过 a 转换为类型 T 的类型 标准转换序列是候选函数。为了 直接初始化,那些显式转换函数 不隐藏在 S 中并产生类型 T 或可以转换的类型 带有资格转换的类型 T 也是候选 职能。返回 cv 限定类型的转换函数是 考虑为此产生该类型的 cv 非限定版本 选择候选函数的过程。 对转化的调用 返回“对 X 的引用”的函数是 X 类型的泛左值,这样的 因此,转换函数被认为产生 X 选择候选函数的过程。

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

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

【讨论】:

以上是关于在 RValue 对象上调用 LValue ref 限定成员函数的主要内容,如果未能解决你的问题,请参考以下文章

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

对左值(lvalue)和右值(rvalue)的两种理解方式

C++ lvalue,prvalue,xvalue,glvalue和rvalue详解(from cppreference)

lvalue和rvalue的区别

C++,在函数中采用 const lvalue 和 rvalue 引用

如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是不是为真?