Johnmph Johnmph - 22 days ago 7
C++ Question

Ambiguous call not avoided by SFINAE

Compiling this code :

#include <iostream>


template <int N>
struct TestClass {
template <int N2, typename std::enable_if<N2 == N, int>::type = 0>
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};

struct HostClass : public TestClass<1>, public TestClass<2> {
};


int main(int argc, const char * argv[]) {
HostClass hostClass;

hostClass.doAction<1>();
hostClass.doAction<2>();

return 0;
}


leads to an ambiguous call error because
doAction
is both in
TestClass<1>
and
TestClass<2>
parent classes.


main.cpp:33:15: Member 'doAction' found in multiple base classes of different types


But
std::enable_if
would not disable this ambiguity ?

Answer

It would (let me say) drop one of the two functions after the substitution.
Anyway, first of all the compiler has to decide which function you intend to use when you invoke it as doAction<1>, then it can go ahead with the substitution and eventually toss away the chosen function because of sfinae.
At the point of the invocation, both of them are valid candidates and the call is actually ambiguous.

Note that, as suggested by @Peregring-lk in the comments, TestClass<1>::doAction and TestClass<2>::doAction are two different functions placed in different namespaces, they are not overloads of the same function.
This is actually a common source of misunderstandings.


You can easily solve the issue as it follows:

#include <iostream>

template <int N>
struct TestClass {
    void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};

struct HostClass : public TestClass<1>, public TestClass<2> {
    template<int N>
    void doAction() { return TestClass<N>::doAction(); }
};


int main(int argc, const char * argv[]) {
    HostClass hostClass;

    hostClass.doAction<1>();
    hostClass.doAction<2>();

    return 0;
}
Comments