Reputation: 489
I'm setting up a variadic template function to be able to call various function overloads on a specific series of classes. So far, I've been able to "break" the compilation when an unsupported class is passed to the function, but I'd like to be able to provide a valid fallback to handle the "unsupported" scenario at runtime.
The current implementation goes like this :
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, typename = std::enable_if_t<is_my_class_v<T>>>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, typename... Ts>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b); // works
run(c); // does not compile
}
Here, the two most promising (but still failing) attempts I've made to have a fallback for run
:
1 - Adding a simple !
in front of is_my_class<T>
, giving me the following error : error C2995: 'void run(T)': function template has already been defined
template<typename T, typename = std::enable_if_t<!is_my_class_v<T>>>
void run(T myClass)
{
printf("Not supported\n");
};
2 - Making a more "primary" template definition, which yields a sad but obvious : error C2668: 'run': ambiguous call to overloaded function
template<typename T>
void run(T myClass)
{
printf("Not supported\n");
};
EDIT I forgot to specify I was looking for a solution also compatible with C++11/14
Upvotes: 2
Views: 751
Reputation: 1526
Even though I'd recommend @cigien 's solution if c++17 is avaiable, I would like to add, that the ambiguity problem can be mitigated pre c++17 by changing the type of the enable_if
template argument, and hence changing the signature of the "fallback function". The following code should work fine:
template<typename T, std::enable_if_t<!is_my_class_v<T>, int> = 0>
//template<typename T>
void run(T myClass) {
printf("error\n");
};
Full code available on CompilerExplorer, tested on GCC and Clang trunk.
I'd also like to add, that in all use cases I can imagine, it is better to have a compile time error (which you could for instance achieve by using a static assertion
instead of SFINAE
).
Here you can read about why the function delcaration is not ambiguous, even when using two "ints" as template arguments.
Upvotes: 2
Reputation: 4713
I wrote following code and it works. I think you just messed up with syntax of SFINAE.
#include <iostream>
#include <type_traits>
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, std::enable_if_t<is_my_class_v<T>,int> = 0>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, std::enable_if_t<not is_my_class_v<T>,int> = 0>
void run(T myClass)
{
printf("Not supported\n");
};
// wrote an extra SFINEA here to ensure that Ts aren't empty - else it might be an ODR issue despite the fact that it compiles
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0,int> = 0>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b);
run(c);
}
It prints
ClassA overload
ClassB overload
Not supported
Upvotes: 1
Reputation: 63142
Have a fallback runOverload
template
template<typename T>
void runOverload(T myClass)
{
printf("Not supported\n");
};
Upvotes: 0