1

Is there any difference between using typename = enable_if_t<...> and enable_if_t<...,bool> = true for SFINAE? I am asking specifically since I stumbled upon what seems like a bug: Compiler error with a fold expression in enable_if_t

So I got curious whether there is any actual difference between the two.

lightxbulb
  • 1,251
  • 12
  • 29
  • 1
    related/dupe: https://stackoverflow.com/questions/15427667/sfinae-working-in-return-type-but-not-as-template-parameter – NathanOliver Jul 10 '19 at 21:10
  • 2
    `typename = enable_if_t<...>` can't be used to disambiguate two functions, because both would have the same default template argument. – Henri Menke Jul 10 '19 at 21:20

2 Answers2

5

There are minor differences, but both can be used for SFINAE.

typename = enable_if_t<...> forms doesn't allow "simple" overloads:

template <typename T, typename = enable_if_t<cond<T>::value>>
void foo();

template <typename T, typename = enable_if_t<!cond<T>::value>>
void foo(); // Error: redeclaration of same function as default are not part of signature
            // Both are just template <typename, typename> void foo()

enable_if_t<cond, bool> = true doesn't suffer of that:

template <typename T, enable_if_t<cond<T>::value, bool> = true>
void foo();

template <typename T, enable_if_t<!cond<T>::value, bool> = true>
void foo();

Another issue with typename = enable_if_t<...> is that usage might be hijacked:

template <typename T, typename = enable_if_t<cond<T>::value>>
void foo();

template <typename T, typename = enable_if_t<cond<T>::value>>
void bar(T);


foo<int>();   // Regular usage, SFINAE occurs
bar(42);      // Regular usage, SFINAE occurs
bar<int>(42); // Possible usage, SFINAE still occurs
// But
foo<int, void>();   // No substitution fails here, so no SFINAE
bar<int, void>(42); // No substitution fails here, so no SFINAE
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

Yes, there is difference. The first one doesn't work, while the second works. The reason for that is that default template parameters are not part of function signature.

By "work" I mean that the first version doesn't remove the function from the set of candidate overloads, which is usually desired goal when enable_if is used.

An example can be found here (Courtesy of @NathanOliver): http://coliru.stacked-crooked.com/a/a15a6f1d0eaff4ab

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • @lightxbulb it doesn't work in a sense that it doesn't remove a function from a set of overload candidates. – SergeyA Jul 10 '19 at 21:09
  • 2
    How does it not? AFAIK it only doesn't work when you are trying to overload the function with multiple function templates. – NathanOliver Jul 10 '19 at 21:11
  • 1
    @NathanOliver I have added some clarifying statement, this is exactly what I meant, and this is (at least in my experience) the very reason to use enable_if. – SergeyA Jul 10 '19 at 21:12
  • 1
    I think you might need to clarify it a bit more. It works [here](http://coliru.stacked-crooked.com/a/dcebab35f1368e5f) in an overloaded context. It's only when you try and add multiple template overloads it fails like [this](http://coliru.stacked-crooked.com/a/a15a6f1d0eaff4ab) – NathanOliver Jul 10 '19 at 21:16
  • @SergeyA I think this is a strange point of view that seems to be contradicted by the standard. See [temp.deduct]/6-8, [temp.over]/1. Deduction is considered to fail, therefore there is no candidate. – Brian Bi Jul 10 '19 at 21:27
  • @Brian I removed my comment for now. I need to do a bit more research on this and I don't have time right now. – SergeyA Jul 10 '19 at 21:29