/*
 * 为什么要特化?
 *     因为编译器认为,对于特定的类型,如果你对某一功能有更好的实现,那么就该听你的。
 * 模板分类:
 *     类模板/函数模板
 * 特化分类:
 *     全特化
 *         全特化就是限定死模板的实现的具体类型。
 *     偏特化
 *         偏特化就是模板如果有多个类型,那么只限定其中一部分,
 *         细分还可以分为: 范围偏特化和个数偏特化。
 *         个数的偏特化从左到右。
 * 优先级:
 *     全特化 > 偏特化 > 泛化
 * 注意事项:
 *         C++标准规定, 函数模板只有泛化与全特化,不能偏特化,
 *         有些编译器实现了,所以可以。
 *
 */

#include <iostream>

    template <typename T1, typename T2>
    class A {
public:
    void function(T1 v1, T2 v2) {
        std::cout << "类模板泛化" << std::endl;
    }
};

template <>  // 类型明确, 全特化,所以参数列表为空
class A<int, double> {
public:
    void function(int v1, double v2) {
        std::cout << "类模板全特化" << std::endl;
    }
};

template <typename T2>
class A<int, T2> {  // 由于指定了一部分, 剩下未指定的需要在参数列表中,否则报错
public:
    void function(int v1, T2 v2) {
        std::cout << "类模板个数偏特化" << std::endl;
    }
};

template <typename T1, typename T2>
class A<T1*, T2*> {  // 这是范围上的偏
public:
    void function(T1* v1, T2* v2) {
        std::cout << "类模板指针偏特化" << std::endl;
    }
};

template <typename T1, typename T2>
class A<T1 const, T2 const> {  // 这是范围上的偏
public:
    void function(T1 v1, T2 v2) {
        std::cout << "类模板const偏特化" << std::endl;
    }
};

void TestClassTemplate() {
    // 泛化
    A<int, int> a;
    a.function(12, 12);

    // 全特化
    A<int, double> aa;
    aa.function(12, 12.3);

    // 个数偏特化
    A<char, int> aaa;
    aaa.function('a', 12);

    int x;
    // 指针范围偏特化
    A<int*, int*> aaaa;
    aaaa.function(&x, &x);

    // const范围偏特化
    A<const int, const int> aaaaa;
    aaaaa.function(11, 22);
}

template <typename T1, typename T2>
void function(T1 v1, T2 v2) {
    std::cout << "函数泛化" << std::endl;
}

template <>
void function(char v1, double v2) {
    std::cout << "函数全特化" << std::endl;
}

// 函数模板不允许有偏特化
/* template<typename T2> */
/* void function(T2 v1, char v2){ */
/*      std::cout << "函数偏特化" << std::endl; */
/* } */

void TestFunctionTemplate() {
    // 泛化
    function(1, 1);
    // 全特化
    function('a', 1.2);
}

int main() {
    TestClassTemplate();
    TestFunctionTemplate();
    return 0;
}