22

在使用 ref 限定函数重载时,我从GCC (4.8.1)Clang (2.9 and 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 限定的右值重载。

4

1 回答 1

32

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

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

—“对 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()是类纯右值。

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

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

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

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

13.3.3.2.3 中描述了三个可能的绑定(const foo&foo&&const foo&&的排名:

标准转换序列S1 是比标准转换序列 S2 更好的转换序列,如果

  • S1 和 S2 是引用绑定,两者都没有引用非静态成员函数的隐式对象参数,声明时没有 ref-qualifier [此异常不适用于此处,它们都有 ref-qualifiers],并且S1 绑定右值引用到一个右值[一个类纯右值是一个右值]并且 S2 绑定一个左值引用

这意味着两者foo&&const foo&&都更好const foo&

  • S1 和 S2 是引用绑定,并且引用所引用的类型除了顶级 cv-qualifiers 之外是相同的类型,并且S2 初始化的引用所引用的类型比由 S1 初始化的引用是指

这意味着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 中。

于 2013-06-16T06:37:38.333 回答