Разрешение перегрузки методов-шаблонов с помощью ref-квалификаторов - PullRequest
0 голосов
/ 04 февраля 2019

Я разрабатываю контейнер с функцией доступа во время компиляции, используя специальный тип.Я также хочу иметь функцию доступа с использованием числа для реализации операций для всех элементов.Таким образом, у меня есть что-то вроде этого:

struct S
{
    template<int I> int& f();
    template<class Q> int& f();
};

Я хочу запретить доступ для временных объектов, поэтому я добавляю перегрузку для доступа типа:

struct S
{
    template<int I> int& f();
    template<class Q> int& f() &;
    template<class Q> int& f() && = delete;
};

Но тогда у меня проблемас компилятором msvc:

<source>(4): error C2560: 'int &Test::f(void) &': cannot overload a member function with ref-qualifier with a member function without ref-qualifier

Однако и gcc, и clang принимают это.Кто прав?

https://godbolt.org/z/4bmA2-

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

MSVC здесь не так.

Соответствующее правило: [over.load] /2.3:

Объявления функций-членов с тем же именем и тем же списком типов параметровпоскольку объявления шаблона функции-члена с тем же именем, тем же списком типов параметров, и одинаковыми списками параметров шаблона не могут быть перегружены, если у любого из них, но не у всех, есть квалификатор ref ([dcl.fct]).

Здесь шаблоны функций имеют разные параметры шаблона (int I и class Q), поэтому это правило не применяется, и нет другого правила, запрещающего их перегрузку.

0 голосов
/ 04 февраля 2019

MSVC здесь правильный.Вы не можете иметь перегрузку как ref-qualifier, так и non-ref-qualifier.

Почему бы и нет?Потому что они неоднозначны.У квалифицированной функции-члена есть спецификатор, указывающий, является ли объект, на который указывает this, lvalue (&) или rvalue (&&).Между тем, функция-член без квалификационных значений принимает значения и lvalues ​​ и r.Это создает неоднозначность, которую компилятор воспринимает как ошибку.

Обратите внимание, что аспект template этого кода - красная сельдь.У вас было бы столько же проблем с более простым:

struct S
{
    int& f();       // non-ref-qualifier
    int& f() &;     // ref-qualifier (this must be lvalue)
    int& f() &&;    // ref-qualifier (this must be rvalue)
};

или даже просто:

struct S
{
    int& f();       // non-ref-qualifier
    int& f() &;     // ref-qualifier (this must be lvalue)
};

Стандарт языка C ++ 14 определяет семантику разрешения кандидата для ссылки.квалифицированные функции-члены в over.match.funcs .В частности, §13.3.1 [4]: ​​

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

  • «lvalue ссылка на cv X ”для функций, объявленных без ref-qualifier или с & ref-qualifier
  • « rvalue ссылка на cv X »для функций, объявленных с помощью && ref-qualifier

, где X - это класс, членом которого является функция, а cv - это cv-уточнение по объявлению функции-члена.[ Пример: Для функции-члена const, равной class X, предполагается, что дополнительный параметр имеет тип «ссылка на const X».- end example ] Для функций преобразования функция считается членом класса аргумента подразумеваемого объекта с целью определения типа параметра неявного объекта.Для функций без преобразования, введенных using-декларацией в производный класс, функция считается членом производного класса с целью определения типа неявного параметра объекта.Для статических функций-членов считается, что неявный параметр объекта соответствует любому объекту (поскольку, если функция выбрана, объект отбрасывается).[ Примечание : фактический тип не установлен для неявного параметра объекта статической функции-члена, и не будет предпринята попытка определить последовательность преобразования для этого параметра ([over.match.best]).- конец примечания ]

и §13.4.1 [5] (выделение добавлено):

Во время разрешения перегрузки подразумеваемоеАргумент объекта неотличим от других аргументов. Однако неявный параметр объекта сохраняет свою идентичность, поскольку для достижения соответствия типа с ним не могут применяться пользовательские преобразования. Для нестатических функций-членов, объявленных без ref-qualifier , применяется дополнительное правило:

  • , даже если неявный параметр объекта не является константным, значение r может быть привязано к параметру при условии, что во всех других отношениях аргумент может быть преобразован в тип неявного параметра объекта .[ Примечание : Тот факт, что такой аргумент является r-значением, не влияет на ранжирование неявных последовательностей преобразования.- конец примечания ]

MSVC имеет два предупреждения специально для этой неоднозначности перегрузки квалификатора ref:

  • Ошибка компилятора C2559
    ' идентификатор ': невозможно перегрузить функцию-член без квалификатора ref с помощью функции-члена с квалификатором ref

  • Ошибка компилятора C2560
    ' идентификатор ': невозможно перегрузить функцию-член с помощью ref-квалификатора функцией-членом без ref-qualifier

Чтобы запретить вызовы функции-члена для временных объектов (rvalues), достаточно просто сделать:

struct S
{
    int& f() &;            // ref-qualifier (this must be lvalue)
    int& f() && = delete;  // ref-qualifier (this must be rvalue)
};

или с шаблонами:

struct S
{
    template<int I> int& f() &;
    template<int I> int& f() && = delete;

    template<class Q> int& f() &;
    template<class Q> int& f() && = delete;
};
...