带有ref-qualifiers的重载解析

27

在使用ref-qualified函数重载时,我发现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();
}
< p > Clang 编译并输出"const rvalue",而GCC认为这是一个不明确的调用,两个 const 限定的函数都是最佳可行候选项。如果我提供所有4个重载,那么两个编译器都会输出"non-const rvalue"

我想知道哪个编译器——如果有的话——做得对,以及相关的标准条款是什么。

注意:之所以这很重要,是因为真正的代码将两个 const 限定的函数声明为constexpr。当然,没有输出到std::cout,并且使用static_cast而不是std::move,以使它们成为有效的constexpr定义。由于在C++11中,constexpr仍然意味着const,因此不能提供示例代码中被注释掉的重载,因为它将重新定义 const 限定的 rvalue 重载。

1个回答

35

首先,根据13.3.1.4,隐式对象参数被视为普通参数:

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

- 对于没有ref-qualifier或者有& ref-qualifier声明的函数,“cv X的左值引用”

- 对于有&& ref-qualifier声明的函数,“cv X的右值引用”

其中X是该函数成员所属的类,cv是成员函数声明时的cv限定符。

因此,你所问的等同于以下内容:

void bar(foo&);
void bar(foo&&);
void bar(const foo&);
void bar(const foo&&);

int main()
{
    bar(foo());
}

foo()表达式是一个类prvalue。

其次,非const左值引用版本无法使用,因为prvalue无法绑定到它上面。

这留下了三个可行的函数进行重载决策。

每个函数都有一个隐式对象参数(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将右值引用绑定到右值 [class prvalue是右值],S2将左值引用绑定

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

  • S1和S2都是引用绑定,并且引用所指的类型除了顶层cv限定符外相同,初始化为S2引用的引用的类型比S1引用的引用的类型更具有cv限定符

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

因此,Clang是正确的,而GCC中存在一个错误。 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中的这个bug似乎纯粹应用于隐式对象参数(具有ref-qualifiers),对于普通参数,它似乎会正确地进行排名,至少在4.7.2版本中如此。


一个rvalue引用在所有情况下都是更好的匹配。这很可能是GGC的ref-qualifier中的一个错误。 - Xeo
此外,这个答案可能会有所帮助。 ;) - Xeo
感谢您详细的回答。我希望我能多次点赞它! - K-ballo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接