Search code examples
c++11sfinaetype-traitsenable-if

How can I "ostensibly-but-not-really" break the one-definition-rule with enable_if and SFINAE?


I want to define template <typename T> struct is_non_negative in one way for integral T's and another way for floating-point T's. Here's what I did:

template<typename T>
struct is_non_negative: public curry_right_hand_side<greater_equal<
    typename std::enable_if<std::is_integral<T>::value, T>::type>, constant<T, 0>> { };

template<typename T>
struct is_non_negative: public curry_right_hand_side<greater_equal<
    typename std::enable_if<std::is_floating_point<T>::value, T>::type>, constant_by_ratio<T, std::ratio<0,1>>> { };

This triggers a compiler error (GCC 4.9.3, -std=c++11):

error: class template "cuda::is_non_negative" has already been defined

and, well, it has, but it also hasn't, since the template instantiations are distinct.

How can I achieve this effect and actually get my code to compile?

Notes:

  • Never mind how I defined constant, constant_by_ratio and curry_right_hand_side - they have been tested and work.
  • If I replace the second is_non_negative with foo then this compiles and is usable - but not with the same identifier.
  • The motivation for the two definitions here is the impossibility of using floating-point values as template parameters, but please don't focus on that aspect of the example.

Solution

  • Maybe you could do something like this:

    template<class T, class = void>
    struct is_non_negative;
    
    template<typename T>
    struct is_non_negative<T, typename std::enable_if<std::is_integral<T>::value>::type>: public curry_right_hand_side<greater_equal<T>, constant<T, 0>> { };
    
    template<typename T>
    struct is_non_negative<T, typename std::enable_if<std::is_floating_point<T>::value>::type>: public curry_right_hand_side<greater_equal<T>, constant_by_ratio<T, std::ratio<0,1>>> { };