使用 ref-qualifiers 解决重载问题

Posted

技术标签:

【中文标题】使用 ref-qualifiers 解决重载问题【英文标题】:Overload resolution with 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 限定的右值重载。

【问题讨论】:

【参考方案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 可能是相关的。 ;) 感谢您的详细解答。我希望我能多次投票!

以上是关于使用 ref-qualifiers 解决重载问题的主要内容,如果未能解决你的问题,请参考以下文章

Django:我如何在 django.db.models.query QuerySet 中重载 Q 以在我的管理器中用于特殊目的

如何用数组重载 ostream 运算符<<?

运算符 + 重载 C++

如何解决 Flutter 热重载问题

使用函数指针解决函数重载二义性调用问题

如何解决使用文字 0 和指针调用重载函数的歧义