Подтвердить что ты не робот

Разрешение перегрузки с ref-qualifiers

Во время работы с перегруженными функциями перегрузки я получаю разные результаты от 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".

Я хотел бы узнать, какой компилятор --if любой - делает правильные вещи, и каковы соответствующие стандартные пьесы в игре.

Примечание. Причина, по которой это действительно имеет значение, заключается в том, что реальный код объявляет как const-квалифицированные функции как constexpr. Конечно, для std::cout нет вывода, а std::move используется static_cast, поэтому они являются допустимыми определениями constexpr. И поскольку в С++ 11 constexpr по-прежнему подразумевается const, перегрузка, прокомментированная в примере кода, не может быть предоставлена, поскольку она переопределяет переопределенную const-переоценку rvalue.

4b9b3361

Ответ 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.