4

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;
}
Evg
  • 25,259
  • 5
  • 41
  • 83
Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59

1 Answers1

4

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)();
Evg
  • 25,259
  • 5
  • 41
  • 83
  • 1
    Thanks for the bug confirmation and workarounds, they work great! Note for future readers: variable templates is C++14, so you must use the first workaround if you are limited to C++11. – Boris Dalstein Jan 15 '20 at 11:00
  • @BorisDalstein, thanks for the note! I added into the answer. – Evg Jan 15 '20 at 11:11
  • 1
    I reported the bug here: https://developercommunity.visualstudio.com/content/problem/885683/sfinae-error-with-member-function-of-class-templat.html – Boris Dalstein Jan 15 '20 at 11:22
  • This is obviously subjective, but I'm not sure we should encourage using the `_v` suffix for user-defined variable templates. Which one looks more readable: `int x = get_max;` or `int x = get_max_v;`? The suffix does make it consistent with many existing traits in the standard, but I believe the reason the standard is littered with these suffixes is that they had no choice (by C++14 the non-suffixed name was already taken). By the way, speaking of conciseness, you may also want to use `enable_if_t` for the C++14 version. – Boris Dalstein Jan 15 '20 at 14:53
  • Oh, and since we're nitpicking, I believe that `static` is redundant with `constexpr` in the variable template version (static at global/namespace scope). You do need it though in the struct template (static at class scope: means something different). – Boris Dalstein Jan 15 '20 at 15:01
  • 1
    @BorisDalstein, personally, I don't like these `_v` suffixes, too. – Evg Jan 15 '20 at 15:02
  • 1
    @BorisDalstein, it should be `inline`, not `static`. Thanks! – Evg Jan 15 '20 at 15:03