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;
};