An ambiguous challenge that SFINAE does not avoid

Compilation of 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; } 

results in an ambiguous call error, because doAction is in the parent classes TestClass<1> and TestClass<2> .

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

But std::enable_if will not disable this ambiguity?

EDIT:

I think the real reason for this ambiguity is the same as in this question:

Why are multi-inherited functions with the same name, but different signatures cannot be considered overloaded functions?

Uncertainty can be resolved, as shown in the answer with the using keyword:

 #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> { using TestClass<1>::doAction; using TestClass<2>::doAction; }; int main(int argc, const char * argv[]) { HostClass hostClass; hostClass.doAction<1>(); // OK, compile hostClass.doAction<2>(); // OK, compile //hostClass.doAction<3>(); // OK, doesn't compile : "candidate template ignored: disabled by 'enable_if' [with N2 = 3]" return 0; } 

I don't know if this was @skypjack's answer, but I still allowed its alternate method.

+5
source share
1 answer

It would (let me say) drop one of the two functions after substitution.
In any case, first of all, the compiler must decide which function you are going to use when you call it as doAction<1> , and then , it can continue to replace it and ultimately discard the selected function due to sfinae.
At the time of the call, both of them are valid candidates, and the call is really ambiguous.

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


You can easily solve the problem as 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; } 
+5
source

Source: https://habr.com/ru/post/1259646/


All Articles