1

This is the code that has the problem:

template <typename=std::enable_if_t<supports_v<std::equal_to<>, T>> >
bool alreadyValue(const T &value) { return this->value == value; }
// alternate case if T does not support equals operator
bool alreadyValue(const T &value) { return false; }

Here's my support definitions:

template<typename F, typename... T, typename = decltype(std::declval<F>()(std::declval<T>()...))>
std::true_type  supports_test(const F&, const T&...);
std::false_type supports_test(...);

template<typename> struct supports;
template<typename F, typename... T> struct supports<F(T...)>
    : decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};

template<typename F, typename T>
constexpr bool supports_v = supports<F(T, T)>::value;

template<typename F, typename... T>
constexpr bool all_supports_v = (supports<F(T, T)>::value && ...);

Now, MSVC 19.20 has no problem with this code.

But GCC 9.1 complains that:

In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = supports_v<std::equal_to<void>, A>; _Tp = void]':

error: no type named 'type' in 'struct std::enable_if<false, void>'

Since SFINAE knows that "no type in struct" should fail silently as its not an error, my question is did I do something wrong?

Here's an example of what I'm working with:

Community
  • 1
  • 1
Rhaokiel
  • 813
  • 6
  • 17
  • 2
    Can you provide a [mcve]? Like the second `alreadyValue` overload should be a template presumably, etc. – Barry Jun 11 '19 at 20:03
  • 1
    I would recommend using `if constexpr` for these kind of cases. Much easier to read. See https://stackoverflow.com/a/51659883/2466431 – JVApen Jun 11 '19 at 20:27
  • @JVApen, thanks, I never knew that I could inline an constexpr like that. – Rhaokiel Jun 11 '19 at 20:32
  • It's a nice new feature of C++17, simplifies a lot of code I used to write – JVApen Jun 11 '19 at 20:33
  • 2
    For SFINAE you need an unevaluated context and dependent types. You seem to have neither. What is it that you're trying to accomplish? As to why MSVC accepts this. Well, it's just not a very good C++ compiler. Especially if you go for these kinds of things. – rubenvb Jun 11 '19 at 20:38
  • 1
    As it is currently presented, T is template param of the class and not the method (obviously). This could be related to the following [question](https://stackoverflow.com/questions/13964447/why-compile-error-with-enable-if). – Mike Spencer Jun 11 '19 at 23:53
  • I get it, the template is **not** an unevaluated context because T is already evaluated at class scope. So the enable_if is not subject to SFINAE. I'd mark that as the answer, @rubenvb. – Rhaokiel Jun 12 '19 at 12:39
  • @Rhaokiel I'll see if I can conjure up some standardese transliterations in the coming days – rubenvb Jun 12 '19 at 15:00

1 Answers1

1

As @rubenvb stated and @Mike Spencer alluded to, the enable_if is not in an unevaluated context because there is no generic type dependent on the function. So no SFINAE.

As a solution, I instead built the function as a generic support function:

template<typename T, bool=supports_v<std::equal_to<>, T>>
struct is_equal_t;
template<typename T>
struct is_equal_t<T, true> {
    bool operator() (const T& x, const T& y) const { return x==y; }
};
template<typename T>
struct is_equal_t<T, false> {
    bool operator() (const T& x, const T& y) const { return false; }
};

template<typename T>
bool is_equal(const T& x, const T& y) {
    static is_equal_t<T> cmp;
    return cmp(x, y);
}
Rhaokiel
  • 813
  • 6
  • 17