The following code produces different results on 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 (versions 4.8.1 - 9.1), icc (versions 16, 17, 19), Visual Studio 2017 15.4.0 Preview 1.0, Visual Studio 2013 12.0.31101.00 Update 4, clang (versions 3.4.1 - 3.9.1)
All give the following output, which is what I expect:
B
C
B
C
B
C
B
C
Only methods C<T>::foo(Dummy1)
and B<T>::foo(Dummy2)
are selected, methods A<T>::foo
are not used.
Clang (versions 4.0.0 - 8.0.0) selects A::foo(Dummy2)
when called through D<T>
object and only then. When called through reference to B<T>
- C<T>::foo(Dummy2)
is selected.
B
A <-- difference
B
C
B
C
B
C
When the order of the derived classes is changed to struct D : C<T>, B<T>
, then output changes to:
A <--
C
B
C
B
C
B
C
It seems that for second derived class method foo
is not considered virtual.
Only Visual Studio is giving warning with not that helpful C4250.
Writing using B<T>::foo;
and using C<T>::foo;
in D<T>
instead of using A::foo;
makes clang produce the following error:
error: call to member function 'foo' is ambiguous
On gcc behavior doesn't change, code compiles and output is the same.
What is correct behavior here?
Since application gives different results, is there a way to find all similar instances of this construction or make some workaround? I have to compile with both gcc and clang. Checking if same problem is present in more places than I found can be hard.