Перегрузка виртуальной функции в алмазной иерархии дает разные результаты в clang и gcc - PullRequest
11 голосов
/ 05 июня 2019

Код Folowing дает разные результаты на Clang.

#include <iostream>

struct Dummy1 {};
struct Dummy2 {};

struct A {
    virtual void foo(Dummy1) {
        std::cout << "A" << std::endl;
    }
    virtual void foo(Dummy2) {
        std::cout << "A" << std::endl;
    }
};


template<class T>
struct C : virtual A {
    using A::foo;
    void foo(Dummy2) override {
        std::cout << "C" << std::endl;
    }   
};


template<class T>
struct B : virtual A {
    using A::foo;
    void foo(Dummy1) final {
        std::cout << "B" << std::endl;
    }   
};

template<class T>
struct D : B<T>, C<T> {
    // using B<T>::foo; // error: call to member function 'foo' is ambiguous
    // using C<T>::foo; // error: call to member function 'foo' is ambiguous
    using A::foo;
};


int main() {
    D<int> d;
    d.foo(Dummy1{});
    d.foo(Dummy2{});

    A& a = d;
    a.foo(Dummy1{});
    a.foo(Dummy2{});

    B<int>& b = d;
    b.foo(Dummy1{});
    b.foo(Dummy2{});


    C<int>& c =d;
    c.foo(Dummy1{});
    c.foo(Dummy2{});

    return 0;
}

gcc (версии 4.8.1 - 9.1), icc (версии 16, 17, 19), Visual Studio 2017 15.4.0 Preview 1.0, Visual Studio 2013 12.0.31101.00 Обновление 4, clang (версии 3.4.1 - 3.9. 1)

все выдают следующий результат, чего я и ожидаю:

B
C
B
C
B
C
B
C

Выбраны только методы C<T>::foo(Dummy1) и B<T>::foo(Dummy2), методы A<T>::foo не используются.

Clang (версии 4.0.0 - 8.0.0) выбирает A::foo(Dummy2) при вызове через объект D<T> и только затем. При вызове через ссылку на B<T> - C<T>::foo(Dummy2) выбран.

B
A <-- difference
B
C
B
C
B
C

Когда порядок производных классов изменяется на struct D : C<T>, B<T>, вывод изменяется на:

A <--
C
B
C
B
C
B
C

Похоже, что для второго производного класса метод foo не считается виртуальным.

Только Visual Studio выдает предупреждение, но не очень полезно C4250 .

Запись using B<T>::foo; и using C<T>::foo; в D<T> вместо using A::foo; заставляет clang выдавать следующую ошибку: error: call to member function 'foo' is ambiguous. На gcc поведение не меняется, код компилируется и вывод одинаков.

Что такое правильное поведение здесь?

Поскольку приложение дает разные результаты, есть ли способ найти все подобные экземпляры этой конструкции или сделать какой-то обходной путь? Я должен скомпилировать как gcc, так и clang. Проверить, присутствует ли та же проблема в большем количестве мест, чем я обнаружил, может быть сложно.

...