В следующей тестовой программе struct B
имеет две функции-члена f
, которые можно вызвать с помощью B{}.f()
: одну обычную f()
, а другую с явным объектом f(this A)
.
struct A {
int f() { return 1; }
};
struct B : A {
using A::f;
int f(this A) { return 2; }
};
int main() {
return B{}.f();
}
Какую функцию необходимо выбрать при разрешении перегрузки?
GCC и MSVC предпочитают обычную функцию-член f()
, и программа возвращает 1
.
Но Clang делает противоположный выбор, выбирая явную функцию-член объекта f(this A)
, и программа возвращает 2
.
Онлайн-демо: https://gcc.godbolt.org/z/1bo69Ta8q
Какой компилятор здесь правильный, если таковой имеется (почему бы не неоднозначность разрешения перегрузки)?
«почему бы не неоднозначность разрешения перегрузки» - Связано: CWG 2789
Вы также можете увидеть это напрямую, добавив вектор копирования для класса A
и заметив, что clang неправильно использует вектор копирования, а gcc и msvc — нет. Демо
Я думаю, если бы вы написали int f(this B)
вместо int f(this A)
, это должно было бы быть двусмысленно, но, похоже, только MSVC придерживается такого же мнения: gcc.godbolt.org/z/W7Yd4Eved
Вот отчет об ошибке clang
тлдр; Clang ошибся в выборе явной функции-члена объекта по причинам, описанным ниже. Вот подтвержденная ошибка с звоном.
Какой компилятор здесь правильный, если таковой имеется (почему бы не неоднозначность разрешения перегрузки)?
Прежде всего обратите внимание, что согласно over.match.func.general, функция, назначенная using A::f
, считается членом производного класса с целью определения типа неявного параметра объекта.
Это означает, что для вызова B{}.f()
лучше подходит неявная функция-член объекта, поскольку она не требует преобразования аргумента B{}
.
Для справки вот over.match.func.general, в котором говорится:
Для функций без преобразования, введенных с помощью объявления using в производный класс, функция считается членом производного класса с целью определения типа неявного параметра объекта.
Мы также можем увидеть это, добавив вектор копирования для класса A
и заметив, что clang неправильно использует вектор копирования для преобразования, а gcc и msvc — нет. Демо
Таким образом, gcc и msvc правы, выбрав неявную версию, которую сделал видимой using A::f;
.
Вот подтвержденная ошибка clang:
Clang выбирает неправильную перегрузку, когда задействованы явные и неявные функции-члены
«Какой компилятор здесь правильный, если таковой имеется (почему бы не неоднозначность разрешения перегрузки)?...» GCC и msvc верны, потому что вызов неявной функции-члена не требует преобразования.