C++模板特化示例和总结

在学习元编程过程中,需要用到模板递归,函数模板和类模板的递归方式略有差别,主要是由于二者特化语法的差别导致的。通过一些例子整理了函数模板和类模板的特化语法和差异。

要点

  1. c++函数模板不支持偏特化,包括类型偏特化、值偏特化都不支持。
  2. 多个参数的函数模板,不能通过特化在模板递归中设置递归的终止条件。
  3. 单个值数值参数的函数模板,可以通过模板特化实现递归终止。此时的是完全特化函数模板,这是允许的。
  4. 不允许函数模板偏特化,对函数的能力影响不大,函数可以通过重载提供对各种类型变化的支持。

1. 函数模板递归

1.1 函数模板用constexpr if实现递归

// 函数模板不支持偏特化,不能通过模板设置递归终止,可以用constexpr if终止递归
template <typename T, int n>
void printTuple(const T& t) {
    
    
    if constexpr (n > 1) {
    
    
        printTuple<T, n - 1>(t);
    }
    cout << get<n-1>(t) << endl;
}

// 不允许偏特化函数模板,编译错误
//template <typename T, int n>
//void printTuple <T, 0> (const T& t) {
    
    
//}

// 给n提供默认值,简化调用
template <typename T, int n=tuple_size<T>::value>
void printTuple2(const T& t) {
    
    
    if constexpr (n > 1) {
    
    
        printTuple<T, n - 1>(t);
    }
    cout << get<n - 1>(t) << endl;
}

int main()
{
    
    
    tuple<int, double, const char*, string> tp{
    
    1, 2.0, "hi", "boy"};
    using atuple = tuple<int, double, const char*, string>;

    printTuple<atuple, tuple_size<atuple>::value>(tp);    
    printTuple2<atuple>(tp);
}

1.2 函数模板通过完全特化实现递归

// 利用函数模板完全特化实现递归展开
template<int N>
int factorial() {
    
    
    return N * factorial<N-1>();
}

template<>
int factorial<0>() {
    
    
    return 1;
}

int main() {
    
    
    std::cout << factorial<5>() << std::endl;  // 输出:120
    return 0;
}

2 类模板递归

2.1 类模板通过偏特化实现递归

类模板可以实现多个参数的模板偏特化,偏特化类型参数、值参数都支持。

// 通过类模板特化实现递归终止
// 也可以使用constexpr if实现终止逻辑,这样就不用特化终止条件的类模板class TuplePrint<T, 0>
// 这里的n也可以提供默认值tuple_size<T>::value
template <typename T, int n>
class TuplePrint
{
    
    
public:
    TuplePrint(const T& t) {
    
    
        //if constexpr (n > 1) {
    
    
            TuplePrint<T, n - 1> tp{
    
     t };
        //}
        cout << get<n - 1>(t) << endl;
    }
};

template <typename T>
class TuplePrint<T, 0>
{
    
    
public:
    TuplePrint(const T& t) {
    
    }
};

int main()
{
    
    
    tuple<int, double, const char*, string> tp{
    
    1, 2.0, "hi", "boy"};
    using atuple = tuple<int, double, const char*, string>;

    TuplePrint<atuple, tuple_size<atuple>::value> tpr{
    
    tp};
}

2.2 类模板通过constexpr if实现递归

// 使用constexpr if实现终止逻辑,这样就不用特化终止条件的类模板class TuplePrint<T, 0>
// 这里的n也可以提供默认值tuple_size<T>::value
template <typename T, int n>
class TuplePrint
{
    
    
public:
    TuplePrint(const T& t) {
    
    
        if constexpr (n > 1) {
    
    
            TuplePrint<T, n - 1> tp{
    
     t };
        }
        cout << get<n - 1>(t) << endl;
    }
};

int main()
{
    
    
    tuple<int, double, const char*, string> tp{
    
    1, 2.0, "hi", "boy"};
    using atuple = tuple<int, double, const char*, string>;

    TuplePrint<atuple, tuple_size<atuple>::value> tpr{
    
    tp};
}

3 模板和特化示例

3.1 类模板的偏特化例子

// 工具方法,打印3个参数,简化类实现
void print(auto a, auto b, auto n) {
    
    
    cout << endl;
    cout << "a: " << typeid(a).name() << endl;
    cout << "b: " << typeid(b).name() << endl;
    cout << "n: " << n << endl;
}

template <typename Ta, typename Tb, int n>
class oneclass
{
    
    
public:
    oneclass(const Ta& a, const Tb& b) {
    
    
        print(a, b, n);
    }
};

template <typename Ta, typename Tb>
class oneclass<Ta, Tb, 10>
{
    
    
public:
    oneclass(const Ta& a, const Tb& b) {
    
    
        print(a, b, 10);
        cout << "specialization for 10 :)" << endl;
    }
};

template <typename Ta, int n>
class oneclass<Ta, int, n>
{
    
    
public:
    oneclass(const Ta& a, const int& b) {
    
    
        print(a, b, n);
        cout << "specialization for Tb :)" << endl;
    }
};

template <typename Tb, int n>
class oneclass<int, Tb, n>
{
    
    
public:
    oneclass(const int& a, const Tb& b) {
    
    
        print(a, b, n);
        cout << "specialization for the 1st type Ta :)" << endl;
    }
};

int main()
{
    
    
    oneclass<double, const char*, 2> oc1 {
    
     1.0, "hi" };
    oneclass<double, const char*, 10> oc2 {
    
     2.0, "hi" };
    oneclass<double, int, 11> oc3 {
    
     1.0, 3 };
    oneclass<int, const char*, 11> oc4 {
    
     4, "hi" };
}

运行结果:

//     oneclass<double, const char*, 2> oc1 { 1.0, "hi" }; 输出
a: double
b: char const * __ptr64
n: 2

//     oneclass<double, const char*, 10> oc2 { 2.0, "hi" }; 输出
a: double
b: char const * __ptr64
n: 10
specialization for 10 :)

//     oneclass<double, int, 11> oc3 { 1.0, 3 }; 输出
a: double
b: int
n: 11
specialization for Tb :)

//     oneclass<int, const char*, 11> oc4 { 4, "hi" }; 输出
a: int
b: char const * __ptr64
n: 11
specialization for the 1st type Ta :)

3.2 函数模板特化例子

template <typename Ta, typename Tb>
void printSum(Ta a, Tb b) {
    
    
    cout << "general: sum=" << a + b << endl;
}

// 函数模板不支持偏特化,error
//template <typename Ta>
//void printSum<Ta, int>(const Ta& a, const int& b) {
    
    
//    cout << "T&int: sum=" << a + b << endl;
//}

template <>
void printSum<int, int>(int a, int b) {
    
    
    cout << "int: sum=" << a + b << endl;
}

template <typename T>
void printSum(T a, T b) {
    
    
    cout << "T: sum=" << a + b << endl;
}

int main()
{
    
    
    printSum(1.0f, 1); // general: sum=2, printSum<float, int>
    printSum(1.0, 2.0); // T: sum=3, printSum<double, double>
    printSum(1, 3); // T: sum=4, printSum<T, T>,这里有限调用<T, T>版本,除非像下面显示调用<int, int>的函数,
    printSum<int, int>(1, 4); // int: sum=5, printSum<int, int>
    printSum<double, int>(1, 5); // general: sum=6, printSum<double, int>
}

4 总结特化语法

  1. 类型参数特化为具体类型。
  2. 非类型参数,值参数,特化为具体数值。
  3. 模板参数列表定义在template关键字后<>中,特化参数列表定义在模板名称(类名或函数名)后的<>中。
  4. 对于特化的模板,模板参数列表中去掉特化的参数。
  5. 在类定义或者函数定义中,用特化的类型名替换对应的通用类型参数名,用具体的特化值,替换对应的非类型参数名。特化的通用类型和非类型参数名,不再有效。

4.1 类模板的特化

// 类模板的定义和特化
template<模板参数列表,包括类型参数、非类型参数>
class ClassName {
    
     ... }

// 完全特化
template<>
class ClassName<特化模板参数列表,包括具体类型、非类型参数的具体值> {
    
     ... }

// 部分特化,偏特化
template<未特化的模板参数列表,包括类型、非类型参数>
class ClassName<未特化的类型或变量名, 特化的具体类型名或具体值> {
    
     ... }

4.2 函数模板特化

// 函数模板的定义和特化
template<模板参数列表,包括类型参数、非类型参数>
returnType func {
    
     ... }

// 完全特化函数模板
template<>
returnType func<特化模板参数列表,包括具体类型、非类型参数的具体值> {
    
     ... }

猜你喜欢

转载自blog.csdn.net/yinminsumeng/article/details/129972777