C++ 函数模板与类模板知识点总结

一、基础概念

1.1 什么是模板

模板是 C++ 的一个特性,使得你可以编写与类型无关的代码。模板通过定义类型参数,使得同一段代码可以适用于多种数据类型。

二、函数模板

2.1 定义与语法
  • 基本定义

  • template <typename T>
    T functionName(T arg) {
        // 函数体
    }

  • 示例代码

  • #include <iostream>
    using namespace std;

    // 定义一个函数模板用于加法
    template <typename T>
    T add(T a, T b) {
        return a + b; // 返回两数之和
    }

    int main() {
        cout << add(5, 10) << endl;          // T 被推导为 int
        cout << add(5.5, 10.5) << endl;      // T 被推导为 double
        return 0;
    }

2.2 使用场景与示例
2.2.1 数学运算

函数模板适合处理不同数值类型的加法、乘法等操作。

// 数学运算示例
template <typename T>
T multiply(T a, T b) {
    return a * b; // 返回两数之积
}

int main() {
    cout << multiply(3, 4) << endl;         // 输出: 12
    cout << multiply(2.5, 4.0) << endl;     // 输出: 10.0
    return 0;
}

2.2.2 排序算法

可以实现一个通用的排序函数,适用于任何可比较的类型。

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

// 排序函数模板
template <typename T>
void sortArray(vector<T>& arr) {
    sort(arr.begin(), arr.end()); // 使用 STL 的 sort 函数
}

int main() {
    vector<int> intArr = {3, 1, 4, 1, 5};
    sortArray(intArr);
    for (const auto& num : intArr) {
        cout << num << " "; // 输出: 1 1 3 4 5
    }
    cout << endl;

    vector<string> strArr = {"banana", "apple", "cherry"};
    sortArray(strArr);
    for (const auto& str : strArr) {
        cout << str << " "; // 输出: apple banana cherry
    }
    return 0;
}

2.2.3 容器操作

自定义容器类以处理任意数据类型。

#include <iostream>
#include <vector>
using namespace std;

// 自定义容器类模板
template <typename T>
class MyContainer {
public:
    void add(const T& item) {
        items.push_back(item); // 添加元素
    }

    void display() const {
        for (const auto& item : items) {
            cout << item << " "; // 输出所有元素
        }
        cout << endl;
    }

private:
    vector<T> items; // 内部存储
};

int main() {
    MyContainer<int> intContainer;
    intContainer.add(1);
    intContainer.add(2);
    intContainer.display(); // 输出: 1 2

    MyContainer<string> strContainer;
    strContainer.add("Hello");
    strContainer.add("World");
    strContainer.display(); // 输出: Hello World

    return 0;
}

2.3 错误处理
  • 类型不匹配
  • // 如果传入不支持的类型,将导致编译错误
    cout << add("Hello", "World"); // 错误:不支持字符串相加
  • 未定义行为
  • template <typename T>
    T divide(T a, T b) {
        return a / b; // 如果 b 为 0,将导致运行时错误
    }
     
2.4 特化
  • 完全特化
  • template <>
    double add<double>(double a, double b) {
        // 针对 double 类型的特化实现
        return a + b; // 可增加特定的逻辑
    }
  • 偏特化
  • template <typename T>
    class MyClass<T*> { // 针对指针类型的特化
    public:
        void print() {
            cout << "This is a pointer type." << endl;
        }
    };
2.5 常见问题
  1. 模板参数不明确

// 当存在多重重载时,编译器可能无法推断参数类型
template <typename T>
void func(T a, T b);

func(1, 2); // 如果同时存在 func(int, float) 会导致编译错误

复杂的错误信息

  • 编译器给出的模板错误信息常常难以理解,特别是当模板嵌套时。

三、类模板

3.1 定义与语法
  • 基本定义
  • template <typename T>
    class ClassName {
        // 成员变量和成员函数
    };
  • #include <iostream>
    using namespace std;

    // 类模板定义,包含一个类型为 T 的成员变量
    template <typename T>
    class Box {
    public:
        Box(T val) : value(val) {} // 构造函数
        T getValue() const { return value; } // 返回值的成员函数

    private:
        T value; // 成员变量
    };

    int main() {
        Box<int> intBox(123); // 使用 int 类型
        Box<string> strBox("Hello"); // 使用 string 类型

        cout << intBox.getValue() << endl; // 输出: 123
        cout << strBox.getValue() << endl; // 输出: Hello
        return 0;
    }

3.2 使用场景与示例
3.2.1 自定义数据结构

适合创建可以处理不同类型的自定义数据结构,如栈、队列。

#include <iostream>
#include <stack>
using namespace std;

// 自定义栈类模板
template <typename T>
class MyStack {
public:
    void push(const T& item) {
        s.push(item);
    }

    void pop() {
        if (!s.empty()) {
            s.pop();
        }
    }

    T top() const {
        return s.top();
    }

private:
    stack<T> s; // 内部使用标准栈
};

int main() {
    MyStack<int> intStack;
    intStack.push(1);
    intStack.push(2);
    cout << intStack.top() << endl; // 输出: 2
    intStack.pop();

    MyStack<string> strStack;
    strStack.push("Hello");
    strStack.push("World");
    cout << strStack.top() << endl; // 输出: World

    return 0;
}

3.3 错误处理
  • 类型不匹配
  • // 使用不支持的类型将导致编译错误
    MyStack<double> doubleStack;
    doubleStack.push("Hello"); // 错误:类型不匹配
3.4 特化
  • 类特化
  • template <>
    class Box<int> { // 针对 int 类型的特化
    public:
        void print() {
            cout << "This is an integer box." << endl;
        }
    };
3.5 常见问题
  1. 友元声明

template <typename T>
class MyClass;

template <typename T>
class FriendClass {
    friend class MyClass<T>; // 友元模板
};
    2.继承问题

template <typename T>
class Base {
    // 基类模板
};

template <typename T>
class Derived : public Base<T> {
    // 派生类模板
};

四、进阶知识

4.1 可变参数模板
  • 支持接受不定数量的参数。
  • #include <iostream>
    using namespace std;

    // 可变参数模板
    template <typename... Args>
    void print(Args... args) {
        (cout << ... << args) << endl; // 使用折叠表达式输出参数
    }

    int main() {
        print(1, 2, 3.5, "Hello"); // 输出: 123.5Hello
        return 0;
    }

4.2 模板元编程
  • 使用模板进行编译时计算。
  • #include <iostream>
    using namespace std;

    // 计算阶乘的模板
    template <int N>
    struct Factorial {
        static const int value = N * Factorial<N - 1>::value; // 递归计算
    };

    template <>
    struct Factorial<0> { // 基础情况
        static const int value = 1;
    };

    int main() {
        cout << "Factorial of 5: " << Factorial<5>::value << endl; // 输出: 120
        return 0;
    }

4.3 C++20 新特性:概念
  • 用于约束模板参数类型,提高代码可读性和安全性
  • #include <iostream>
    #include <concepts>
    using namespace std;

    // 定义一个概念,要求类型 T 必须支持加法
    template <typename T>
    concept Addable = requires(T a, T b) {
        { a + b };
    };

    // 使用概念限制模板参数
    template <Addable T>
    T add(T a, T b) {
        return a + b; // 只允许支持加法的类型
    }

    int main() {
        cout << add(5, 10) << endl; // 输出: 15
        return 0;
    }

五、最佳实践与常见陷阱

  1. 避免过度特化

    • 过度使用特化会使代码更复杂,增加维护难度。
    • 示例:// 不建议为每个类型都特化
      template <>
      class Box<double> {
          // 不必要的特化
      };
  2. 合理使用 decltypeauto:使用 decltype 可以根据表达式自动推导类型。

template <typename T>
void func(T arg) {
    decltype(arg) newArg = arg; // 根据 arg 类型推导 newArg
}

 使用类型特征

  • 在模板中使用类型特征可以增加代码的类型安全性。
  • 示例:#include <type_traits>
    template <typename T>
    void func(T arg) {
        static_assert(std::is_integral<T>::value, "Type must be integral"); // 仅支持整型
    }
  • 简化模板参数

    • 复杂的模板参数会导致代码可读性下降,尽量使用简单的类型或自定义类型。
  • 调试复杂模板

    • 使用 static_assert 捕捉错误。
    • 示例:template <typename T>
      class MyClass {
      public:
          static_assert(std::is_integral<T>::value, "T must be an integral type"); // 仅支持整型
      };

六.补充知识

特化与偏特化

1. 特化(Full Specialization)

定义: 特化是指为特定类型提供完全不同的实现。当你为一个特定类型定义一个模板的特殊版本时,称为特化。

理解: 假设你有一个模板函数或类,它可以处理多种类型,但你希望为特定的类型(如 intdouble)提供特定的实现,以优化性能或改变逻辑。

#include <iostream>
using namespace std;

// 基本函数模板
template <typename T>
T add(T a, T b) {
    return a + b; // 通用实现
}

// 完全特化:针对 int 类型的特化
template <>
int add<int>(int a, int b) {
    cout << "Using specialized add for int" << endl;
    return a + b; // 特化实现
}

int main() {
    cout << add(5, 10) << endl; // 使用特化,输出: Using specialized add for int
    cout << add(2.5, 3.5) << endl; // 使用通用实现,输出: 6
    return 0;
}

2. 偏特化(Partial Specialization)

定义: 偏特化是指对模板的部分参数进行特化,而不必提供所有参数的具体类型。它通常用于模板类。

理解: 偏特化可以让你为一组类型提供特殊的处理,而不是针对单一类型。这种特化可以帮助你处理更具体的情况。

示例

#include <iostream>
using namespace std;

// 基本类模板
template <typename T>
class MyClass {
public:
    void show() {
        cout << "Generic MyClass" << endl;
    }
};

// 偏特化:当类型为指针时
template <typename T>
class MyClass<T*> {
public:
    void show() {
        cout << "MyClass specialized for pointers" << endl;
    }
};

int main() {
    MyClass<int> obj1; // 使用通用实现
    obj1.show(); // 输出: Generic MyClass

    MyClass<int*> obj2; // 使用偏特化
    obj2.show(); // 输出: MyClass specialized for pointers

    return 0;
}

3. 使用场景
  • 特化
    • 当你需要对某些类型有不同的处理逻辑时,例如当 intdouble 处理方式不同。
  • 偏特化
    • 当你想要为某一类类型(如所有指针类型)提供特定实现时,可以使用偏特化,而无需为每个具体类型实现一遍。
4. 总结
  • 特化是为单一特定类型定义的实现,通常用于优化和改变逻辑。
  • 偏特化是为某一类型的子集定义的实现,允许更灵活的处理多种类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值