4

I wonder what is the difference between using std::enable_if as function argument vs template argument?

I have the following 2 function templates:

#include <type_traits>

template<typename T>
void f_function(T, typename std::enable_if_t<std::is_pod<T>::value, int> = 0)
{
}

template<typename T, typename = typename std::enable_if_t<std::is_pod<T>::value>>
void f_template(T)
{
}

int main()
{
  int x = 1;
  f_function(x);
  f_template(x);
}

which produce the following assembly (as from https://godbolt.org/g/ON4Rya):

main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movl    $1, -4(%rbp)
        movl    -4(%rbp), %eax
        movl    $0, %esi
        movl    %eax, %edi
        call    void f_function<int>(int, std::enable_if<std::is_pod<int>::value, int>::type)
        movl    -4(%rbp), %eax
        movl    %eax, %edi
        call    void f_template<int, void>(int)
        movl    $0, %eax
        leave
        ret
void f_function<int>(int, std::enable_if<std::is_pod<int>::value, int>::type):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        nop
        popq    %rbp
        ret
void f_template<int, void>(int):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        nop
        popq    %rbp
        ret

Besides the obvious difference being that f_function having 2 function parameters and f_template having 2 template arguments what are the differences between them? Is there any particular use of one over another?

Patryk
  • 22,602
  • 44
  • 128
  • 244
  • 1
    You might even add it to return type. Apart from the stack needed for your first version (f_function), these behave quite the same. – lorro Jun 20 '16 at 14:58

1 Answers1

3

As a trivial example, you can do this:

int main() {
    // f_function(std::string{}); // (1)
    // f_template<std::string>(std::string{}); // (2)
    f_template<std::string, void>(std::string{});
}

While (1) and (2) do not compile for obvious reasons (std::string is not an accepted type), f_template can be used with a trick even if T is not a pod type.


A valid alternative would be:

template<typename T, std::enable_if_t<std::is_pod<T>::value>* = nullptr>
void f_template(T)
{ }

Another one could be:

template<typename T>
std::enable_if_t<std::is_pod<T>::value>
f_template(T)
{ }

A more obscure one that involves a parameter pack as a guard instead:

template<typename T, typename..., typename = typename std::enable_if_t<std::is_pod<T>::value>>
void f_template(T)
{ }

All these ones work as expected and you cannot work around them (at least, I don't know how to do that, but maybe someone will come with a good trick).

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • It misses in the more obscure one the check that the pack is empty :-) . – Jarod42 Jun 20 '16 at 18:17
  • @Jarod42 It makes sense to have an error at compile time, but also it's worth it to look at the face of the developers that tries to break it using the most improbable sequence of types!! ;-) – skypjack Jun 20 '16 at 18:34
  • @Jarod42 Added and mentioned. Thank you. – skypjack Jun 20 '16 at 18:39
  • That check renders the template ill-formed NDR (because every valid specialization requires an empty pack). Don't do it. – T.C. Jun 20 '16 at 20:01
  • @T.C. Damnit!! Thank you both, for the suggestion and for the anti-suggestion!! :-) – skypjack Jun 20 '16 at 20:04
  • 1
    @T.C.: I don't get your point, the whole point to `...` is to be empty here. – Jarod42 Jun 21 '16 at 10:16