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<特化模板参数列表,包括具体类型、非类型参数的具体值> { ... }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抓饼先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值