C++ 为什么在成员函数上为*指定左值引用不同于不指定任何内容?

C++ 为什么在成员函数上为*指定左值引用不同于不指定任何内容?,c++,c++11,C++,C++11,考虑以下代码: #include <iostream> using namespace std; struct A { void f() { cout << "A::f" << endl; } void f() const { cout << "A::f const" << endl; } }; struct B { void f() & { cout << "B::f &" &l

考虑以下代码:

#include <iostream>
using namespace std;

struct A {
    void f() { cout << "A::f" << endl; }
    void f() const { cout << "A::f const" << endl; }
};

struct B {
    void f() & { cout << "B::f &" << endl; }
    void f() const & { cout << "B::f const &" << endl; }
};

A getA() { return A{}; }
B getB() { return B{}; }

int main() {
    getA().f();
    getB().f();
}
对于
B
,将选择常量重载,而不是非常量重载。我猜这意味着为*指定一个左值ref限定符与根本不指定任何东西是不同的。为什么呢?“implicit this argument”是否改变了类型,并且const重载现在成为重载解析中更好的候选者?

在这种情况下:

struct A {
    void f() { cout << "A::f" << endl; }
    void f() const { cout << "A::f const" << endl; }
};

getA().f();
第一个重载要求
为左值。但是在
getB().f()
中,
getB()
的结果是一个prvalue,它不能绑定到非常量左值。因此,这个重载是不可行的,也不是选择的


然而,第二个重载要求
这个
是常量左值,prvalue可以绑定到它:这个重载是可行的,并且由编译器选择。

作为参考,完全误导我的句子在N3337的
[over.match.funcs]
,§13.3.1/4中:

  • 对于非静态成员函数,隐式对象参数的类型为
  • -对于未使用ref限定符或使用&ref限定符声明的函数,“对cv X的左值引用”

    -使用&&ref限定符声明的函数的“对cv X的右值引用”

    (我的重点)

    所以我开始疯狂地思考为什么输出会有差异。不带或带
    &
    ref限定符应与此处相同,对吗

    好吧,原因是后来在§13.3.1/5中潜入了一条附加规则

    对于未使用ref限定符声明的非静态成员函数,另一条规则适用:

    -即使隐式对象参数不是const限定的,只要在所有其他方面参数可以转换为隐式对象参数的类型,右值也可以绑定到该参数


    这基本上触发了从非常量右值到非常量左值的转换,并使上述示例中的所有差异产生。D'uh.

    B::f
    在a
    B&
    上被调用。由于无法将右值引用转换为左值引用(除非您是visual studio),因此无法调用
    B::f&
    。我不知道
    A::f
    是否与
    A::f&&
    @nwp相同,
    A::f()&
    只能在右值上调用。这里的技巧是ref限定符
    const&
    可以绑定到右值!(使它变得相当无用)我完全理解这一部分,但是关于不在
    f()
    上指定引用限定符的问题?基本上,现在的要点是说
    &
    等同于没有ref限定符是错误的。当然,添加ref限定符与根本没有ref限定符是不同的,否则该功能的要点是什么?!是什么让你想到“
    &
    相当于没有ref限定符”可能首先是真的?我的观点是,为了解决重载问题,应该平等对待它们。但现在,在第24次重读《标准》之后,我想我已经明白了我的推理是错误的。我将发布一个答案供参考。谢谢你让我重新思考。@JonathanWakely:你现在也可以重载左值/右值。@peppe,是的,但是
    &
    限定符限制左值,而没有限定符完全不限制。有一些限制显然不同于没有限制!通常,如果我对某些C++11/14特性的工作原理感到困惑,我会查看提出该特性的文件,该文件有一个措辞部分,显示了对标准所做的所有更改,通常是提案背后的基本原理或指向包含基本原理的早期版本的指针。在这种情况下,它将是。GCC人员有一个方便的列表,列出了链接到相应论文的所有内容和功能。
    struct A {
        void f() { cout << "A::f" << endl; }
        void f() const { cout << "A::f const" << endl; }
    };
    
    getA().f();
    
    struct B {
        void f() & { cout << "B::f &" << endl; }
        void f() const & { cout << "B::f const &" << endl; }
    };
    
    getB().f();