Search code examples
c++templatesenable-if

partial template specialization with enable_if


I am trying to create a function and constrain it to is_trait_types which is defined as:

template <typename T>
struct is_trait_type : std::false_type
{
};

template <>
struct is_trait_type<my_type> : std::true_type
{
};

I want to provide a default implementation for all is_trait_types and then using partial specialization custom behavior for other_types (which obviously are also is_trait_types.

This is the function in question:

// default impl for all is_trait_types
template <typename T, std::enable_if_t<is_trait_type<T>{}, int> = 0>
inline bool should_delay_aggregation(typename T::aggregation, const typename T::type&)
{
    return false;
}

// partial specialization for other_types
template<>
inline bool should_delay_aggregation<other_type>(other_type::aggregation, const other_type::type&)
{
    return some logic with aggregation and type;
}

But I can't get it to compile I get the classic template-id ‘should_delay_aggregation<other_type>’ for ‘bool should_delay_aggregation(other_type::aggregation, const other_type::type&)’ does not match any template declaration, candidate is: ‘template<class T, typename std::enable_if<is_trait_type<T>{}, int>::type <anonymous> > bool should_delay_aggregation(typename T::aggregation, const typename T::type&)

I do need to use std::enable_if since I using C++14. I know that with concepts this would be much easier, but I am not too familiar with std::enable_if.

Thanks in advance.


Solution

  • There are no partial specializations of function templates (Why function template cannot be partially specialized?), and even if there were, you wouldn't be using the right syntax.

    You could write a full specialization of a function template:

    template <typename T>
    auto should_delay_aggregation(typename T::aggregation, const typename T::type&)
      -> std::enable_if_t<is_trait_type<T>{}, bool>
    {
        return false;
    }
    
    // note: if you remove template <>, this simply becomes an overload instead
    //       of a full specialization, which is also okay
    template <>
    bool should_delay_aggregation(other_type::aggregation, const other_type::type&)
    {
        return /* ... */;
    }
    

    However, this isn't very idiomatic; type traits are usually implemented as a class for multiple reasons, and this would also allow you to avoid std::enable_if_t:

    template <bool B> // use std::bool_constant in C++17
    using bool_constant = std::integral_constant<bool, B>;
    
    // primary template
    template <typename T> 
    struct should_delay_aggregation : bool_constant<std::is_same<T, my_type>::value> {};
    // note: is_trait_type can be simplified to std:is_same<T, my_type>
    
    // full specialization for other_type
    template <>
    struct should_delay_aggregation<other_type> : bool_constant</* some logic here */> {};
    
    // other partial specializations are also possible ...
    
    // usage:
    static_assert(should_delay_aggregation<other_type>::value, ":(");