Home > Enterprise >  Default fallback for C template functions using enable_if
Default fallback for C template functions using enable_if

Time:01-07

I want to write a C mechanism, where different instantiations of a function are called if a given class Param is derived from a certain base class.

This works pretty nicely with std::is_base_of and std::enable_if.

However, I would like to have a "default version" of this doStuff() function that is called for "every other class". This would basically work by doing something like "if Param is not derived from A and if not derived from B", but I wonder whether there is a more elegant solution.

#include <iostream>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() {std::cout << "Base A" << std::endl;};

template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() {std::cout << "Base X" << std::endl;};


int main()
{
    doStuff<B>();
    doStuff<Y>();
    // doStuff<Other>(); this is neither derived from B and Y, so call the default case
}

The solution should work with C 14.

CodePudding user response:

When using std:::enable_if, you will have to provide a 3rd SFINAE'd overload that handles the default conditions which are not handled by the other overloads, eg:

#include <iostream>
#include <type_traits>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() { std::cout << "Base A" << std::endl; }

template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() { std::cout << "Base X" << std::endl; }

template <typename Param, std::enable_if_t<!(std::is_base_of<A, Param>::value || std::is_base_of<X, Param>::value), bool> = true>
void doStuff() { std::cout << "Something else" << std::endl; }

int main()
{
    doStuff<B>(); // prints "Base A"
    doStuff<Y>(); // prints "Base X"
    doStuff<Other>(); // prints "Something else"
}

Online Demo

That being said, in C 17 and later, you can use if constexpr instead, which is cleaner than using SFINAE in this situation, eg:

#include <iostream>
#include <type_traits>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param>
void doStuff() {
    if constexpr (std::is_base_of_v<A, Param>)
        std::cout << "Base A" << std::endl;
    else if constexpr (std::is_base_of_v<X, Param>)
        std::cout << "Base X" << std::endl;
    else
        std::cout << "Something else" << std::endl;
}

int main()
{
    doStuff<B>(); // prints "Base A"
    doStuff<Y>(); // prints "Base X"
    doStuff<Other>(); // prints "Something else"
}

Online Demo

CodePudding user response:

Or if you can use C 17 or later use constexpr, a lot more readable then SFINAE

#include <type_traits>
#include <iostream>

class A {};
class B : public A {};

class X {};
class Y : public X {};

class Other {};

template<typename type_t>
void doStuff()
{ 
    if constexpr (std::is_base_of_v<A,type_t>)
    {
        std::cout << "Base A\n";
        
    }
    else
    if constexpr (std::is_base_of_v<X, type_t>)
    {
        std::cout << "Base X\n";
    }
    else
    {
        std::cout << "Other\n";
    }
};

int main()
{
    doStuff<B>();
    doStuff<Y>();
    doStuff<Other>(); //this is neither derived from B and Y, so call the default case

    return 0;
}
  •  Tags:  
  • Related