在使用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限定的rvalue重载。 / p>
答案 0 :(得分:28)
首先,根据13.3.1.4:
将隐式对象参数视为普通参数对于非静态成员函数,隐式对象参数的类型为
- 对于没有引用限定符或使用&amp;而声明的函数的“对cv X的左值引用” REF-限定符
对于用&amp;&amp ;;声明的函数,- “对cv X的rvalue引用” REF-限定符
其中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&&
),所以我们必须对这三个进行排名以确定最佳匹配。
在所有三种情况下,它都是直接绑定的引用绑定。这在declarators / initialization(8.5.3)中有描述。
三种可能的绑定(const foo&
,foo&&
和const foo&&
)的排名在13.3.3.2.3中描述:
标准转换序列 S1是比标准转换序列S2更好的转换序列,如果
- S1和S2是引用绑定,并且都没有引用没有ref-qualifier声明的非静态成员函数的隐式对象参数[此异常不适用于此处,它们都具有ref-qualifiers],并且 S1将右值引用绑定到右值 [类prvalue是右值] ,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中。