[英]Overload resolution with ref-qualifiers
While working with ref-qualified function overloads, I'm getting different results from GCC (4.8.1) and Clang (2.9 and trunk) . 在使用ref限定的函数重载时,我从GCC(4.8.1)和Clang(2.9和trunk)获得了不同的结果。 Consider the following code: 考虑以下代码:
#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 compiles it and outputs "const rvalue"
, while GCC thinks this is an ambiguous call with the two const-qualified functions both being best viable candidates. Clang编译它并输出"const rvalue"
,而GCC认为这是一个模棱两可的调用,两个const限定的函数都是最佳可行的候选者。 If I provide all 4 overloads, then both compilers output "non-const rvalue"
. 如果我提供所有4个重载,则两个编译器都将输出"non-const rvalue"
。
I would like to know which compiler --if any-- is doing the right thing, and what are the relevant standard pieces in play. 我想知道哪个编译器(如果有)在做正确的事情,以及相关的标准部分在起作用。
Note: The reason this actually matters is that the real code declares both const-qualified functions as constexpr
. 注意:这实际上很重要的原因是实际代码将两个const限定函数都声明为constexpr
。 Of course, there is no output to std::cout
and static_cast
is used instead of std::move
, so that they are valid constexpr
definitions. 当然,没有输出到std::cout
并且使用了static_cast
而不是std::move
,因此它们是有效的constexpr
定义。 And since in C++11 constexpr
still implies const
, the overload commented out in the sample code cannot be provided as it would redefine the const-qualified rvalue overload. 并且由于在C ++ 11中 constexpr
仍然隐含const
,因此无法提供示例代码中注释的重载,因为它将重新定义const限定的右值重载。
Firstly, the implicit object parameter is treated as a normal parameter as per 13.3.1.4: 首先,根据13.3.1.4,将隐式对象参数视为普通参数:
For non-static member functions, the type of the implicit object parameter is 对于非静态成员函数,隐式对象参数的类型为
— “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier —“不带ref限定符或带有&ref限定符声明的函数的“对cv X的左值引用”
— “rvalue reference to cv X” for functions declared with the && ref-qualifier —“用&v引用限定符声明的函数的cv X的右值引用”
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration. 其中X是该函数是成员的类,而cv是该成员函数声明上的cv限定词。
So what you are asking is equivalent to the following: 因此,您要问的内容等同于以下内容:
void bar(foo&);
void bar(foo&&);
void bar(const foo&);
void bar(const foo&&);
int main()
{
bar(foo());
}
The expression foo()
is a class prvalue. 表达式foo()
是类prvalue。
Secondly, the non-const lvalue reference version is not viable, as a prvalue cannot bind to it. 其次,非常量左值引用版本不可行,因为prvalue不能绑定到它。
This leaves us with three viable functions for overload resolution. 这为我们提供了三个可行的函数来解决过载。
Each has a single implicit object parameter ( const foo&
, foo&&
or const foo&&
), so we must rank these three to determine the best match. 每个都有一个隐式对象参数( const foo&
, foo&&
或const foo&&
),因此我们必须对这三个参数进行排名以确定最佳匹配。
In all three case it is a directly bound reference binding. 在所有三种情况下,它都是直接绑定的参考绑定。 This is described in declarators/initialization (8.5.3). 这在声明符/初始化(8.5.3)中进行了描述。
The ranking of the three possible bindings ( const foo&
, foo&&
and const foo&&
) is described in 13.3.3.2.3: 13.3.3.2.3中描述了三种可能的绑定( const foo&
, foo&&
和const foo&&
)的排名:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if 如果满足以下条件,则标准转换序列S1比标准转换序列S2更好。
- S1 and S2 are reference bindings and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier [this exception doesn't apply here, they all have ref-qualifiers], and S1 binds an rvalue reference to an rvalue [a class prvalue is an rvalue] and S2 binds an lvalue reference . S1和S2是引用绑定,都没有引用未声明ref限定符的非静态成员函数的隐式对象参数[此异常不适用于此处,它们都具有ref限定符], S1绑定了右值引用到一个右值 [一个类prvalue是一个右值] ,S2绑定一个左值引用 。
This means that both foo&&
and const foo&&
are better then const foo&
. 这意味着foo&&
和const foo&&
比const foo&
更好。
- S1 and S2 are reference bindings, and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers . S1和S2是引用绑定,除了顶级cv限定符之外,引用所引用的类型是相同的类型,并且由S2初始化的引用所引用的类型比sc和s2所引用的类型更具cv资格。由S1初始化的引用为 。
This means that foo&&
is better than const foo&&
. 这意味着foo&&
比const foo&&
更好。
So Clang is right, and it is a bug in GCC. 因此Clang是正确的,这是GCC中的错误。 The overload ranking for foo().bar()
is as follows: 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;
};
The bug in GCC seems to apply purely to implicit object parameters (with ref-qualifiers
), for a normal parameter it seems to get the ranking correct, at least in 4.7.2. GCC中的错误似乎仅适用于隐式对象参数(使用ref-qualifiers
),对于正常参数而言,至少在4.7.2中,该排名似乎正确。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.