Search code examples
c++templates

Is there a way to choose between different class implementations based on the type of the template argument?


I want to create an array for example. The array accepts as a template argument the type to deal with. Now if the type is primitive (int, double, short) then shallow copy can be used, while if it is a class the copy constructor of the class needs to be used. If it was one of the methods it was easy, I can use SFAINE with std::enable_if on the return value, but it is not the case with constructors.

I have tried the following but GCC-9 gives a compilation error :

template< typename Type, typename std::enable_if< std::is_integral< Type >::value() >::type* = nullptr >
class Array
{
    ...Implementation...
}

template< typename Type, typename std::enable_if< std::is_integral< Type >::value() == false >::type* = nullptr >
class Array
{
    ...Implementation 2...
}

Furthermore, I tried for the default constructor but GCC-9 still is not happy :

template< typename Type >
class Array
{

    Array( typename std::enable_if< std::is_integral< Type >::value() >::type* = nullptr )
    {}

    Array( typename std::enable_if< std::is_integral< Type >::value() == false >::type* = nullptr )
    {}

}

Is there a way to make a class with the same name that have different implementation based on the type of the template argument given?

There seem to be questions similar but not exactly by my opinion, if I am wrong tell me and I'll delete the question


Solution

  • Way to specialize thanks to SFINAE is to have an template parameter for that:

    template<typename Type, typename Enabler = void> class Array;
    
    template<typename Type>
    class Array<Type, typename std::enable_if<std::is_integral< Type >::value() >::type>
    {
        // ...Implementation 1...
    };
    
    template<typename Type>
    class Array<Type, typename std::enable_if<!std::is_integral< Type >::value() >::type>
    {
        // ...Implementation 2...
    };
    

    C++20 allows to specialize without extra template parameter thanks to concept/constraint:

    template <typename Type> class Array;
    
    template <typename Type>
    requires (std::is_integral<Type>::value())
    class Array<Type>
    {
        // ...Implementation 1...
    };
    
    template <typename Type>
    requires (!std::is_integral<Type>::value())
    class Array<Type>
    {
        // ...Implementation 2...
    };