使用 std::enable_if 作为函数参数与模板参数有什么区别?

What is the difference between using std::enable_if as function argument vs template argument?

我想知道使用 std::enable_if 作为函数参数与模板参数有什么区别?

我有以下两个函数模板:

#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);
}

生成以下程序集(从 https://godbolt.org/g/ON4Rya 开始):

main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    , %rsp
        movl    , -4(%rbp)
        movl    -4(%rbp), %eax
        movl    [=12=], %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    [=12=], %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

除了明显的区别是 f_function 有 2 个函数参数和 f_template 有 2 个模板参数之外,它们之间还有什么区别?两者之间有什么特别的用途吗?

作为一个简单的例子,您可以这样做:

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

虽然 (1)(2) 由于显而易见的原因无法编译(std::string 不是可接受的类型),即使 T 不是 pod 类型,f_template 也可以与技巧一起使用。


一个有效的替代方案是:

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

另一个可能是:

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

一个更晦涩的涉及参数包作为保护的方法:

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

所有这些都按预期工作,你无法绕过它们(至少,我不知道该怎么做,但也许有人会带来一个好技巧)。