带reff限定符的重载解析

Overload resolution with ref-qualifiers

本文关键字:重载 reff      更新时间:2023-10-16

在使用refqualified函数重载时,我从GCC (4.8.1)Clang(2.9和trunk)得到不同的结果。考虑下面的代码:

#include <iostream>
#include <utility>
struct foo
{
    int& bar() &
    {
        std::cout << "non-const lvalue" << std::endl;
        return _bar;
    }
    //~ int&& bar() &&
    //~ {
    //~     std::cout << "non-const rvalue" << std::endl;
    //~     return std::move(_bar);
    //~ }
    int const& bar() const &
    {
        std::cout << "const lvalue" << std::endl;
        return _bar;
    }
    int const&& bar() const &&
    {
        std::cout << "const rvalue" << std::endl;
        return std::move(_bar);
    }
    int _bar;
};
int main(int argc, char** argv)
{
    foo().bar();
}

Clang编译它并输出"const rvalue",而GCC认为这是一个模棱两可的调用,两个const限定函数都是最可行的候选者。如果我提供所有4个重载,那么两个编译器都输出"non-const rvalue"

我想知道哪个编译器——如果有的话——正在做正确的事情,以及相关的标准部分是什么。

注意:这实际上很重要的原因是实际代码将两个const限定函数声明为constexpr。当然,没有输出到std::cout,并且使用static_cast代替std::move,因此它们是有效的constexpr定义。由于在 c++ 11中 constexpr仍然意味着const,因此不能提供在示例代码中注释掉的重载,因为它将重新定义const限定的右值重载。

首先,隐式object参数被视为13.3.1.4中的正常参数:

对于非静态成员函数,隐式对象形参的类型为

- "对cv X的左值引用"对于没有引用限定符或带有&ref-qualifier

-使用&&声明的函数的"对cv X的右值引用"ref-qualifier

,其中X是函数所属的类,cv是成员的cv-限定条件函数声明。

所以你问的是等价于以下内容:

void bar(foo&);
void bar(foo&&);
void bar(const foo&);
void bar(const foo&&);
int main()
{
    bar(foo());
}

表达式foo()是一个类的右值。

其次,非const左值引用版本是不可行的,因为右值不能绑定到它。

这给我们留下了三个可行的重载解析函数。

每个都有一个隐式对象参数(const foo&, foo&&const foo&&),所以我们必须对这三个进行排序以确定最佳匹配。

在这三种情况下,它都是直接绑定引用绑定。这在声明器/初始化(8.5.3)中有描述。

三种可能的绑定(const foo&, foo&&const foo&&)的排序在13.3.3.2.3中描述:

标准转换序列S1优于标准转换序列S2,如果

  • S1和S2都是引用绑定,它们都不指向未声明ref-qualifier的非静态成员函数的隐式对象形参[此处不适用此例外,它们都有ref-qualifier], S1将右值引用绑定到右值[类的prvalue是右值], S2绑定左值引用

这意味着foo&&const foo&&都比const foo&好。

  • S1和S2是引用绑定,除了顶级的cv限定符之外,引用所指向的类型是相同的,并且S2初始化的引用所指向的类型比S1初始化的引用所指向的类型更符合cv限定。

这意味着foo&&const foo&&更好。

所以Clang是对的,这是GCC中的一个bug。foo().bar()的过载排名如下:

struct foo
{
    int&& bar() &&;             // VIABLE - BEST  (1)
    int const&& bar() const &&; // VIABLE -       (2)
    int const& bar() const &;   // VIABLE - WORST (3)
    int& bar() &;               // NOT VIABLE
    int _bar;
};

GCC中的错误似乎纯粹适用于隐式对象参数(与ref-qualifiers),对于正常参数,它似乎得到正确的排名,至少在4.7.2中。