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

Почему перегрузка на одном рефлектонаторе не разрешена?

По-видимому, перегрузка на ref-qualifiers не разрешена - этот код не будет компилироваться, если вы удалите либо &, либо && (только токены, а не их функции):

#include <iostream>

struct S {
    void f() &  { std::cout << "Lvalue" << std::endl; }
    void f() && { std::cout << "Rvalue" << std::endl; }
};

int main()
{
    S s;
    s.f();   // prints "Lvalue"
    S().f(); // prints "Rvalue"
}

Другими словами, если у вас есть две функции с одним и тем же именем и типом, вы должны определить оба, если вы определяете либо. Я предполагаю, что это преднамеренно, но какая причина? Почему бы не разрешить, скажем, вызывать версию && для rvalues, если она определена, и "первичный" f() для всего остального в следующем варианте (и наоборот - хотя это будет запутать):

struct S {
    void f()    { std::cout << "Lvalue" << std::endl; }
    void f() && { std::cout << "Rvalue" << std::endl; }
};

Другими словами, пусть они действуют аналогично специализированным шаблонам в отношении первичного шаблона.

4b9b3361

Ответ 1

Это ничем не отличается от следующей ситуации:

struct S {};

void g(S s);
void g(S& s);

int main()
{
    S s;
    g(s);     // ambiguous
}

Разрешение перегрузки всегда срабатывало таким образом; передача по ссылке не является предпочтительной для передачи по значению (или наоборот).

(Разрешение перегрузки для ref-квалифицированных функций работает так, как если бы это была нормальная функция с неявным первым параметром, аргумент которого *this; lvalue-ref квалифицирован как первый параметр S &, const & t24 > и т.д.)

Думаю, вы говорите, что g(s) должен называть g(S&) вместо двусмысленного.

Я не знаю точного обоснования, но разрешение перегрузки достаточно сложное, так как оно не добавляет больше особых случаев (особенно те, которые могут без труда компилировать не то, что предполагал кодер).

Как вы заметили в своем вопросе, проблему можно легко избежать, используя две версии S & и S &&.

Ответ 2

У вас может быть и то, и другое. Нет специального требования для включения обоих, если вы реализуете один.

Ловушка заключается в том, что метод-член (нестатический член), который не помечен квалификатором, подходит для использования как с l-значениями, так и с r-значениями. Как только вы перегрузите метод с помощью ref-qualifier, если вы не отметите других, вы столкнетесь с проблемами двусмысленности.

При разрешении перегрузки нестатическая функция-член класса cv класса X рассматривается как функция, которая принимает неявный параметр ссылки типа lvalue на cv-qualified X, если у него нет реф-квалификаторов или если он имеет ref-квалификатор lvalue. В противном случае (если он имеет rvalue ref-qualifier), он рассматривается как функция, принимающая неявный параметр ссылки на тип rvalue на cv-квалифицированный X.

В принципе, если у вас есть один метод, который квалифицирован (например, для lvalue &) и тот, который не является квалифицированным, правила таковы, что они эффективно и квалифицированы, и поэтому неоднозначны.

Подобное обоснование применяется к классификатору const. Вы можете реализовать метод и иметь одну "версию" для объекта const и один для объекта const. Стандартными библиотечными контейнерами являются хорошие примеры этого, в частности методы begin(), end() и другие итераторы.

Один конкретный случай использования - когда логика, применяемая к методу, отличается между тем, когда объект является временным (или истекающим) объектом, а когда нет. Возможно, вам захочется оптимизировать некоторые вызовы и обработку данных, если вы знаете, что срок службы близок к завершению.

Другим является ограничение использования метода для lvalues. Определенная часть прикладной или объектной логики может не иметь смысла или быть полезной, если объект истекает или является временным.


Формулировка в стандарте (взятая из проекта N4567) из §13.4.1/4:

Для нестатических функций-членов тип неявного параметра объекта

  • "lvalue reference to cv X" для функций, объявленных без ref-qualifier или с рефлектором-определителем

  • "rvalue reference to cv X" для функций, объявленных с помощью && & & реф-классификатор

Ответ 3

Начнем с того, что означает определение базовой нестатической функции-члена без каких-либо рефлексификаторов.

& sect; 13.3.1 [4]

Для нестатических функций-членов тип неявного параметра объекта

— " ссылка lvalue на cv X" для объявленных функций без ref-qualifier или с рефлектором-определителем

— "rvalue reference to cv X" для функций, объявленных с помощью && & реф-классификатор

Но подождите, там еще.

[5] Для нестатических функций-членов, объявленных без ref-qualifier, применяется следующее правило:

даже если параметр неявного объекта не является const-квалифицированным, значение rvalue может быть привязано к параметру как как и во всех других отношениях, аргумент может быть преобразован в тип неявного параметра объекта.

Поэтому

  • Вы можете перегрузить только один тип ref: lvalue, rvalue
  • Вы не можете перегружать одну или другую, а затем добавлять в другую, которая не соответствует требованиям, потому что тот, который не соответствует критериям, привязан к обоим типам и, следовательно, к двусмысленности.