SFINAE

Substitution Failure Is Not An Error : 替换失败不是错误
*** 在模板的参数替换过程中,如果某个模板参数导致不合法的替换,编译器并不会立即报错,而是尝试寻找其他匹配的模板。 ***
当编译器在实例化一个模板时,进行模板参数替换,如果替换过程中发生了错误(例如类型不匹配、表达式非法等),编译器会忽略这个模板,而不会将其视为编译错误。
这时,编译器会继续寻找其他可能匹配的模板。如果没有找到其他匹配的模板,编译才会失败。
SFINAE 主要用于函数模板的重载,通过某些条件选择合适的模板函数,而忽略不适用的函数模板。

typename

template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
process(T value) {
std::cout << "Processing integral type: " << value << std::endl;
}

typename 关键字的作用是在模板中显式声明一个依赖于模板参数的类型。在这里,std::enable_if<std::is_integral::value, void>::type
是依赖于模板参数 T 的类型,因此需要使用 typename 来告诉编译器 type 是一个类型,而不是成员或其他符号。

模板

  1. 模板
  2. 模板全特化 : 全特化意味着你为某个具体类型完全重新定义模板的实现,而不是使用模板的默认实现。
    #include <iostream>
    using namespace std;

    // 普通函数模板
    template <typename T>
    T add(T a, T b) {
    return a + b;
    }

    // 函数模板的全特化:为 const char* 特化
    template <>
    const char* add(const char* a, const char* b) {
    return "This is a specialized version for const char*";
    }
  3. 模板偏特化 : 在某些类型的基础上进行部分特化,而不是完全特化。它允许对部分模板参数进行特定类型的处理,而不需要完全指定所有参数的类型。
    偏特化不能用于函数模板,只能用于类模板。偏特化常见的用法是在部分模板参数上进行特化处理。
    #include <iostream>
    using namespace std;

    // 定义一个类模板,接受两个类型参数
    template <typename T1, typename T2>
    class MyClass {
    public:
    MyClass() {
    cout << "Generic template version" << endl;
    }
    };

    // 对类模板进行偏特化:当第二个类型参数为 int 时
    template <typename T1>
    class MyClass<T1, int> {
    public:
    MyClass() {
    cout << "Partial specialization for T2 = int" << endl;
    }
    };
    // 对指针类型进行偏特化
    template <typename T>
    class MyClass<T*> {
    public:
    MyClass() {
    cout << "Partial specialization for pointers" << endl;
    }
    };

一个阶乘的简单例子

#include <iostream>

using namespace std;

template <int n> struct factorial {
static_assert(n >= 0, "Arg must be non-negative");
static const int value = n * factorial<n - 1>::value;
};

template <> struct factorial<0> { static const int value = 1; };

int main() {

printf("%d\n", factorial<10>::value);
return 0;
}