Ответ 1
Во-первых, параметр неявного объекта рассматривается как нормальный параметр согласно 13.3.1.4:
Для нестатических функций-членов тип неявного параметра объекта
- "lvalue reference to cv X" для функций, объявленных без ref-определителя или с рефлектором-определителем
- "rvalue reference to cv X" для функций, объявленных с помощью && & реф-классификатор
где X - класс, членом которого является член, а cv - cv-квалификация на члене объявления функции.
Итак, то, что вы просите, эквивалентно следующему:
void bar(foo&);
void bar(foo&&);
void bar(const foo&);
void bar(const foo&&);
int main()
{
bar(foo());
}
Выражение foo()
является классом prvalue.
Во-вторых, эталонная версия non-const lvalue не является жизнеспособной, поскольку prvalue не может связываться с ней.
Это оставляет нам три жизнеспособные функции для разрешения перегрузки.
Каждый из них имеет один неявный параметр объекта (const foo&
, foo&&
или const foo&&
), поэтому мы должны оценивать эти три, чтобы определить наилучшее соответствие.
Во всех трех случаях это непосредственно связанное связывание ссылок. Это описано в деклараторах/инициализации (8.5.3).
Ранжирование трех возможных привязок (const foo&
, foo&&
и const foo&&
) описано в 13.3.3.2.3:
Стандартная последовательность преобразования S1 - лучшая последовательность преобразования, чем стандартная последовательность преобразования S2, если
- S1 и S2 являются привязками привязок, и ни один из них не ссылается на неявный параметр объекта нестатической функции-члена, объявленной без ref-qualifier [это исключение не применяется здесь, все они имеют ref-квалификаторы] и S1 связывает ссылку rvalue с rvalue [класс prvalue является rvalue] , а S2 связывает ссылку lvalue.
Это означает, что как foo&&
, так и const foo&&
лучше, чем const foo&
.
- S1 и S2 являются привязками привязки, и типы, к которым относятся ссылки, относятся к одному типу, за исключением квалификаторов cv верхнего уровня, и тип, к которому ссылается ссылка, инициализированная символом S2, является более cv-qualified чем тип, к которому ссылка, инициализированная S1, относится к.
Это означает, что foo&&
лучше, чем const foo&&
.
Итак, Кланг прав, и это ошибка в 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.