【问题标题】:Overload resolution with ref-qualifiers使用 ref-qualifiers 解决重载问题
【发布时间】:2013-06-12 09:48:24
【问题描述】:

在使用 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();
}

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

我想知道哪个编译器--如果有的话-- 正在做正确的事情,还有哪些相关的标准在起作用。

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

【问题讨论】:

    标签: c++ c++11 overloading language-lawyer ref-qualifier


    【解决方案1】:

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

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

    —“对 cv X 的左值引用”,用于声明没有 ref-qualifier 或 & 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 foo&amp;foo&amp;&amp;const foo&amp;&amp;),因此我们必须对这三个进行排名以确定最佳匹配。

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

    三个可能的绑定(const foo&amp;foo&amp;&amp;const foo&amp;&amp;)的排名在 13.3.3.2.3 中描述:

    标准转换序列S1是比标准转换序列S2更好的转换序列 if

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

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

    • S1和S2是引用绑定,引用所引用的类型除了顶级的cv限定符外是相同的类型,而S2初始化的引用所引用的类型更具有cv限定比由 S1 初始化的引用所引用的类型

    这意味着foo&amp;&amp; 优于const foo&amp;&amp;

    所以 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 中的错误似乎仅适用于隐式对象参数(使用ref-qualifiers),对于普通参数,它似乎得到了正确的排名,至少在 4.7.2 中。

    【讨论】:

    • 右值引用在所有情况下都是更好的匹配。这可能是 GGC 的 ref-qualifiers 的错误。
    • 另外,this answer 可能是相关的。 ;)
    • 感谢您的详细解答。我希望我能多次投票!
    猜你喜欢
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 2019-09-21
    • 1970-01-01
    • 2019-10-31
    • 2011-05-06
    相关资源
    最近更新 更多