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

Неоднозначно, когда два суперкласса имеют функцию-член с тем же именем, но разные подписи

struct A {
    void f(int x) {}
};

struct B {
    template<typename T> void f(T x) {}
};

struct C : public A, public B {};

struct D {
    void f(int x){}
    template<typename T> void f(T x) {} 
};


int main(int argc, char **argv) {
    C c;
    c.f<int>(3);
    D d;
    d.f<int>(3);
}

В чем причина, по которой вызов d.f прав, но c.f дает

error: request for member ‘f’ is ambiguous
error: candidates are: template<class T> void B::f(T)
error:                 void A::f(int)
4b9b3361

Ответ 1

Первая часть связана с поиском имени участника, поэтому она терпит неудачу.

Я бы назвал вас: 10.2/2 Member name lookup

Следующие шаги определяют результат поиска имени в области класса, C. Во-первых, каждое объявление для имени в классе и в каждом из рассматриваются его под-объекты базового класса. Имя члена f в одном под-объект B скрывает имя члена f в под-объекте A, если A является базой class под-объектом B. Любые декларации, которые так скрыты, исключается из рассмотрения. Каждое из этих заявлений, которое было введенные с помощью декларации использования, считаются от каждого под-объект C, который имеет тип, содержащий объявление обозначенный декларацией использования.

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

Теперь, что касается функций шаблона.

Согласно 13.3.1/7 Candidate functions and argument list

В каждом случае, когда кандидат является шаблоном функции, кандидат специализированные шаблоны функций генерируются с использованием шаблона вывод аргумента (14.8.3, 14.8.2). Затем эти кандидаты обрабатываются поскольку кандидат действует обычным способом. Данное имя может относиться к одному или больше шаблонов функций, а также к набору перегруженных функции без шаблона. В этом случае функции кандидата созданные из каждого шаблона функции, объединены с набором не-шаблонные функции-кандидаты.

И если вы продолжаете читать 13.3.3/1 Best viable function

F1 считается лучшей функцией, если:

F1 - функция без шаблона, а F2 - шаблон функции специализации

Вот почему следующий фрагмент компиляции и запускает функцию без шаблона без ошибок:

D c;
c.f(1);

Ответ 2

Я считаю, что компилятор предпочитает A::f (функция без шаблона) над B::f без всякой причины.
Это скорее компилятор ошибка реализации больше, чем зависящая от реализации деталь.

Если вы добавите следующую строку, то компиляция прекрасна и выбрана правильная функция B::f<>:

struct C : public A, public B { 
  using A::f; // optional
  using B::f;
};

[Смешно, что до тех пор, пока ::f не попадут в область C, они рассматриваются как чуждые функции.]

Ответ 3

Компилятор не знает, какой метод вызывать из класса C, потому что шаблонный метод будет транслироваться в void f (int) в случае типа int, поэтому у вас есть два метода с тем же именем и теми же аргументами, но члены разных родительских классов.

template<typename T> void f(T x) {} 

или

void f(int)

попробуйте следующее:

c.B::f<int>(3);

или это для класса A:

c.A::f(3);

Ответ 4

Рассмотрим этот более простой пример:

struct A{
 void f(int x){}
};

struct B{
 void f(float t){}
};


struct C:public A,public B{
};

struct D{
 void f(float n){}
 void f(int n){}
};


int main(){
 C c;
 c.f(3);

 D d;
 d.f(3);
}

В этом примере, как и у вас, D компилируется, но C нет.
Если класс является производным, механизм поиска элементов ведет себя по-разному. Он проверяет каждый базовый класс и объединяет их: в случае C; Каждый базовый класс соответствует поиску (A:: f (int) и B:: f (float)). После их слияния C решает, что они неоднозначны.

Для класса case D: int выбрана версия вместо float, потому что параметр является целым числом.

Ответ 5

Что, вероятно, происходит, так это то, что экземпляр шаблона происходит отдельно для классов A и B, что заканчивается двумя функциями void f(int).

Это не происходит в D, поскольку компилятор знает о функции void f(int) как специализацию и поэтому не специализируется на T для int.