C++的模板简介

文章目录

    • 一、前言
    • 二、函数模板(Function Template)
    • 三、类模板(Class Template)
    • 四、变参模板(Variadic Template)
    • 五、模板的递归与元编程
    • 六、模板的局限与陷阱
    • 七、常用模板的实例
    • 八、C++20 的概念(Concepts)
    • 九、总结

一、前言

C++的模板(Template)是C++中一种强大的特性,它允许编写泛型程序,即编写不依赖于具体类型的代码。模板提供了一种机制,可以在编译时生成针对不同数据类型的代码,而不需要重复编写相同逻辑的代码。这种机制使得代码更加灵活和可重用。

二、函数模板(Function Template)

函数模板允许我们编写与具体类型无关的函数,从而使得函数能够处理不同的数据类型。函数模板的语法格式如下:

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
    // 函数体
}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 10, y = 20;
    double a = 5.5, b = 2.3;
    
    std::cout << max(x, y) << std::endl;  // 输出 20
    std::cout << max(a, b) << std::endl;  // 输出 5.5
    
    return 0;
}

在这里,typename T 表示模板参数T,它是一个占位符,表示函数中可以使用任意类型。当我们调用这个函数时,编译器会根据传递的参数类型自动推导出T的具体类型。

  • 模板类型参数与非类型模板参数

除了类型参数外,C++还支持非类型模板参数。非类型模板参数通常是一个整数或指针值,可以在编译时确定。例如:

template <typename T, int size>
T arraySum(T (&arr)[size]) {
    T sum = 0;
    for (int i = 0; i < size; ++i) {
        sum += arr[i];
    }
    return sum;
}

在这个例子中,size 是一个非类型模板参数,表示数组的大小。

  • 显式指定模板参数

虽然编译器通常可以推导出模板参数,但也可以显式地指定它们:

int a = max<int>(3, 7);

三、类模板(Class Template)

类模板允许定义一个通用的类,然后可以用不同的数据类型来实例化该类。典型的例子是标准库中的 std::vector 类,它是一个类模板,可以用来存储任意类型的元素。它的语法格式跟类模板类型,下面是一个简单的类模板示例:

template <typename T>
class Pair {
public:
    Pair(T first, T second) : first_(first), second_(second) {}
    
    T getFirst() const { return first_; }
    T getSecond() const { return second_; }
    
private:
    T first_, second_;
};

int main() {
    Pair<int> intPair(1, 2);
    Pair<std::string> stringPair("hello", "world");
    
    std::cout << intPair.getFirst() << ", " << intPair.getSecond() << std::endl;  // 输出 1, 2
    std::cout << stringPair.getFirst() << ", " << stringPair.getSecond() << std::endl;  // 输出 hello, world
    
    return 0;
}

  • 模板特化(Template Specialization)

模板特化允许我们为特定类型提供模板的特殊实现。模板特化有两种形式:全特化部分特化全特化是为模板的特定类型提供完全不同的实现,下面这个例子实例化Pair< bool >时就会调用特化实现而不是通用实现:

template <typename T>
class Pair {
public:
    Pair(T first, T second) : first_(first), second_(second) {}
    
    T getFirst() const { return first_; }
    T getSecond() const { return second_; }
    
private:
    T first_, second_;
};

template <>
class Pair<bool> {
public:
    Pair(bool first, bool second) : bits((first ? 1 : 0) | (second ? 2 : 0)) {}
    
    bool getFirst() const { return bits & 1; }
    bool getSecond() const { return bits & 2; }
    
private:
    int bits;
};

部分特化只适用于类模板,而不是函数模板,它允许为一部分模板参数提供特定实现:

template <typename T, typename U>
class Pair {
public:
    Pair(T first, U second) : first_(first), second_(second) {}
    void print() { std::cout << first_ << ", " << second_ << std::endl; }
private:
    T first_;
    U second_;
};

// 部分特化版本,针对相同类型的 Pair
template <typename T>
class Pair<T, T> {
public:
    Pair(T first, T second) : first_(first), second_(second) {}
    void print() { std::cout << "Matching types: " << first_ << ", " << second_ << std::endl; }
private:
    T first_;
    T second_;
};

在这个例子中,当 Pair 的两个模板参数类型相同时,编译器会选择部分特化版本,而不是通用版本。

四、变参模板(Variadic Template)

C++11 引入了变参模板,允许模板接受可变数量的模板参数。这对处理不定数量的参数或类型非常有用。

template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << std::endl;
}

在这个例子中,print函数可以接受任意数量的参数,并将它们打印出来。

五、模板的递归与元编程

模板递归和元编程是一种高级用法,允许在编译时进行复杂的计算和逻辑处理。典型的例子是计算编译时的斐波那契数列:

template<int N>
struct Fibonacci {
    static const int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

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

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

这种编译期计算被称为模板元编程Template Metaprogramming),用于在编译期执行逻辑判断和计算。

六、模板的局限与陷阱

编译时间: 模板实例化会增加编译时间,尤其是对于大型代码库。
代码膨胀: 模板实例化会导致二进制文件膨胀,因为每种类型的实例化都会生成独立的代码。
错误信息复杂: 当模板代码出错时,编译器产生的错误信息通常较为复杂且难以理解。

七、常用模板的实例

STL 是 C++ 标准库的一部分,其中包含了大量的模板,如 std::vector, std::map, std::set 等。这些模板类使得开发者能够方便地使用各种数据结构和算法,而不必为每种类型单独实现。

八、C++20 的概念(Concepts)

C++20 引入了概念(Concepts),使得模板的类型约束更加明确。概念是一种用于限制模板参数的特性,可以显著提高模板代码的可读性和可维护性。

template <typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

template <Addable T>
T add(T a, T b) {
    return a + b;
}

通过引入概念,编译器可以更早地检测出不满足条件的模板参数,从而减少复杂的编译错误信息。

九、总结

C++模板是一种功能强大且灵活的机制,能够极大地提高代码的复用性和类型安全性。模板的学习曲线较为陡峭,但一旦掌握,它将成为编写高效、通用代码的利器。C++20 的概念进一步增强了模板的表达能力,使得模板编程更加直观和强大。

你可能感兴趣的:(c++,模板,开发语言,C++11,新特性)