使用 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++11 中constexpr
仍然暗示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&
、foo&&
或 const foo&&
),因此我们必须对这三个进行排名以确定最佳匹配。
在所有三种情况下,它都是直接绑定引用绑定。这在声明器/初始化 (8.5.3) 中有描述。
三个可能的绑定(const foo&
、foo&&
和 const foo&&
)的排名在 13.3.3.2.3 中描述:
标准转换序列S1是比标准转换序列S2更好的转换序列 if
S1 和 S2 是引用绑定,它们都没有引用非静态成员函数的隐式对象参数,声明时没有 ref-qualifier [此异常不适用于此处,它们都有 ref-qualifiers],并且 S1 将右值引用绑定到右值 [类纯右值是右值]S2 绑定左值引用。
这意味着foo&&
和const foo&&
都比const foo&
更好。
S1和S2是引用绑定,引用所引用的类型除了***的cv限定符外是相同的类型,而S2初始化的引用所引用的类型更具有cv限定比由 S1 初始化的引用所引用的类型。
这意味着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 中的错误似乎仅适用于隐式对象参数(使用ref-qualifiers
),对于普通参数,它似乎得到了正确的排名,至少在 4.7.2 中。
【讨论】:
右值引用在所有情况下都是更好的匹配。这可能是 GGC 的 ref-qualifiers 的错误。 另外,this answer 可能是相关的。 ;) 感谢您的详细解答。我希望我能多次投票!以上是关于使用 ref-qualifiers 解决重载问题的主要内容,如果未能解决你的问题,请参考以下文章
Django:我如何在 django.db.models.query QuerySet 中重载 Q 以在我的管理器中用于特殊目的