c++visual-c++sfinaeenable-ifnumeric-limits

SFINAE with numeric_limits<T>::max() on MSVC2017


The following code:

template <typename T, typename U>
typename std::enable_if<
    std::numeric_limits<T>::max() == std::numeric_limits<U>::max(),
    bool>::type
same_max() {
    return true;
}

template <typename T, typename U>
typename std::enable_if<
    std::numeric_limits<T>::max() != std::numeric_limits<U>::max(),
    bool>::type
same_max() {
    return false;
}

doesn't compile on MSVC2017 (OK on gcc/clang), with the following error:

error C2995: 'std::enable_if<,bool>::type same_max(void)': function template has already been defined

Is this a problem with my SFINAE, or is this a bug in MSVC?

Note: using std::numeric_limits<T>::is_signed (or std::is_signed<T>::value ) instead of std::numeric_limits<T>::max() compiles fine:

template <typename T, typename U>
typename std::enable_if<
    std::is_signed<T>::value == std::is_signed<U>::value,
    bool>::type
same_signedness() {
    return true;
}

template <typename T, typename U>
typename std::enable_if<
    std::is_signed<T>::value != std::is_signed<U>::value,
    bool>::type
same_signedness() {
    return false;
}

Solution

  • This definitely looks like a bug in the compiler. It doesn't accept member functions in SFINAE (note that the code in the question fails not only for min()/max(), but for any member function like epsilon() or lowest()), but it does accept member constants. You can use the following simple workaround with an additional level of indirection using struct (if limited to C++11):

    template<typename T>
    struct get_max {
        static constexpr T value = std::numeric_limits<T>::max();
    };
    
    template <typename T, typename U>
    typename std::enable_if<get_max<T>::value == get_max<U>::value, bool>::type
    same_max() {
        return true;
    }
    
    template <typename T, typename U>
    typename std::enable_if<get_max<T>::value != get_max<U>::value, bool>::type
    same_max() {
        return false;
    }
    

    or a variable template (since C++14):

    template<typename T>
    inline constexpr T get_max_v = std::numeric_limits<T>::max();
    
    template <typename T, typename U>
    std::enable_if_t<get_max_v<T> == get_max_v<U>, bool>
    same_max() {
        return true;
    }
    
    template <typename T, typename U>
    std::enable_if_t<get_max_v<T> != get_max_v<U>, bool>
    same_max() {
        return false;
    }
    

    To avoid potential problems with min/max macros defined in some headers (like windef.h), the function name can be parenthesized:

    ... = (std::numeric_limits<T>::max)();