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 
//void printTuple  (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
// 这里的n也可以提供默认值tuple_size::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
// 这里的n也可以提供默认值tuple_size::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 oc1 { 1.0, "hi" }; 输出
a: double
b: char const * __ptr64
n: 2

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

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

//     oneclass 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 
//void printSum(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
    printSum(1.0, 2.0); // T: sum=3, printSum
    printSum(1, 3); // T: sum=4, printSum,这里有限调用版本,除非像下面显示调用的函数,
    printSum<int, int>(1, 4); // int: sum=5, printSum
    printSum<double, int>(1, 5); // general: sum=6, printSum
}

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<特化模板参数列表,包括具体类型、非类型参数的具体值> { ... }

你可能感兴趣的:(C++,Again,Android开发技术专栏,c++,开发语言)