Search code examples
c++templatesenable-if

Type definition should be dependent on template parameters


Given a template class A, I want to define a type T_res depending on A's template parameters T_lhs and T_rhs:

template< typename T_lhs, typename T_rhs >
class A
{
  // definition of T_res in case "T_lhs and T_rhs are both not primitive types"
  template< bool lhs_is_fundamental = std::is_fundamental<T_lhs>::value,
            bool rhs_is_fundamental = std::is_fundamental<T_rhs>::value,
            std::enable_if_t<( !lhs_is_fundamental && !rhs_is_fundamental )>* = nullptr >
  using T_res = decltype( std::declval<T_lhs>().cast_to_primitive() / std::declval<T_rhs>().cast_to_primitive() );

  // definition of T_res in case "T_lhs and/or T_rhs is a primitive type"
  template< bool lhs_is_fundamental = std::is_fundamental<T_lhs>::value,
            bool rhs_is_fundamental = std::is_fundamental<T_rhs>::value,
            std::enable_if_t<( lhs_is_fundamental || rhs_is_fundamental )>* = nullptr >
  using T_res = decltype( std::declval<T_lhs>() / std::declval<T_rhs>() );

  // ...
};

In the first case, where T_lhs and T_rhs are both not primitiv types, my code is so designed that they represent classes which implement a function cast_to_primitive()that returns a primitive type; in this first case, I want T_res to have the type that is obtained by dividing an element of type decltype( std::declval<T_lhs>().cast_to_primitive() ) by an element of the type decltype( std::declval<T_rhs>().cast_to_primitive() ).

In the second, where either T_lhs or T_rhs is a primitive type (or even both) , I want T_res to have the type that is obtained by dividing an element of type T_lhs by an element of the type T_rhs. For example, in case that T_lhs is a primitive type, my code is so designed that T_rhs can be implicitly casted to an element of type T_lhs; the same applies in case that T_rhs is primitive.

Unfortunately, the code above does not compile. Error:

error: template non-type parameter has a different type 'std::enable_if_t<(lhs_is_fundamental || rhs_is_fundamental)> *' (aka 'typename enable_if<(lhs_is_fundamental || rhs_is_fundamental), void>::type *') in template redeclaration
            std::enable_if_t<( lhs_is_fundamental || rhs_is_fundamental )>* = nullptr >
                                                                            ^
note: previous non-type template parameter with type 'std::enable_if_t<(!lhs_is_fundamental && !rhs_is_fundamental)> *' (aka 'typename enable_if<(!lhs_is_fundamental && !rhs_is_fundamental), void>::type *') is here
            std::enable_if_t<( !lhs_is_fundamental && !rhs_is_fundamental )>* = nullptr >
                                                                              ^

Can someone help me to fix the problem?


Solution

  • This is roughly the same problem found in Using std::conditional_t to define a class' typedef in dependence of its template parameter

    The solution is to use std::conditional along with helper classes to delay instantiation of the division/call to cast_to_primitive until after std::conditional is instantiated:

    #include <type_traits>
    
    template<class T1, class T2>
    struct A
    {
      template<class T=T1, class U=T2>
      struct cast_to_primitive_t {using type=decltype(std::declval<T>().cast_to_primitive() / std::declval<U>().cast_to_primitive());};
      template<class T=T1, class U=T2>
      struct primitive_div_t {using type=decltype(std::declval<T>() / std::declval<U>());};
    
      using T_res = typename std::conditional_t<std::is_fundamental<T1>{} && std::is_fundamental<T2>{}, primitive_div_t<>, cast_to_primitive_t<>>::type;
    };