泛型编程之模板

1.函数模板

重要行:template<typename T, typename T1>

关键值class和typename含义相同,那么我们以后就使用typename即可。

 

一般情况下的格式:

template<模板参数列表>

返回值类型 函数名(函数参数)

 

模板参数列表的理解:

  函数参数列表在运行时调用者用实参来初始化形参

  而对于模板参数列表,编译器用实际的不同版本的模板实参,代替,对应的模板参数,来实例化不同版本的模板。

两种模板参数:

  模板类型参数:(书上这样命名很奇怪,他对应的有非类型模板参数,所以我还是喜欢称呼他为类型模板参数),可以用来指定返回类型,函数参数类型,以及在函数体内使用。

  非类型模板参数: 模板实参必须是常量表达式,编译器使用字面常量来代替他们。

注:

模板在被使用的时候编译器才会生成代码,即“实例化”后。所以模板的头文件一般同时包括模板的声明和定义。模板要被实例化,那么他的一切都应当是可见的,所有模板作用域内名字都应该找到他的声明。

如果模板函数内部部分实现不支持推导的类型,我们在实例化模板的时候才会报错,所以编译和链接阶段都可能报错,因为处于不同编译单元的函数可能在链接阶段才被真正使用,在编译阶段我们可以只看到声明。(不重要,反正都是报错0.0)

模板函数的函数参数不强制需要使用模板参数

// AI生成
#include <iostream> // 定义一个函数模板,接受一个类型参数 T 和一个整数参数 N // 含有非类型模板参数,使用的时候需要尖括号指定 template <typename T, int N> void printNTimes(const T& value) { for (int i = 0; i < N; ++i) { std::cout << value << " "; } std::cout << std::endl; } int main() { // 使用函数模板,类型参数为 int,非类型参数为 5 printNTimes<int, 5>(42); // 输出 "42 42 42 42 42 " // 使用函数模板,类型参数为 double,非类型参数为 3 printNTimes<double, 3>(3.14); // 输出 "3.14 3.14 3.14 " return 0; }
// AI生成
#include <iostream>

template <typename T>
void printNTimes(const T& value, int N) {
    for (int i = 0; i < N; ++i) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
}

int main() {
    // 调用函数模板,不需要尖括号
    printNTimes(42, 5); // 输出 "42 42 42 42 42 "

    // 调用函数模板,不需要尖括号
    printNTimes(3.14, 3); // 输出 "3.14 3.14 3.14 "

    return 0;
}

 

2.类模板

与函数模板不同的是,编译器不能为类模板推断模板参数类型,需要我们利用尖括号提供额外信息(ps:毕竟类不像函数在调用的时候有实参列表),这些额外信息称之为显式模板实参。

 

类模板成员函数的实例化

类模板的成员函数只在使用时才被实例化(包括static成员函数),这意味着只有在实际调用该成员函数时,编译器才会生成相应的代码。这种延迟实例化的机制有助于减少编译时间和代码大小。

而且这一特性使得即使某种类型不能完全符合模板操作要求,我们依然可以使用该类型实例化类,只不过我们不去使用他的“非法”成员。

// AI
#include <iostream>

template <typename T>
class MyClass {
public:
    void printValue(T value) {
        std::cout << "Value: " << value << std::endl;
    }
};

int main() {
    MyClass<int> intInstance;
    intInstance.printValue(5); // 实例化 printValue<int>(int)

    MyClass<double> doubleInstance;
    doubleInstance.printValue(3.14); // 实例化 printValue<double>(double)

    return 0;
}

 

注:在类模板的作用域内,我们可以直接类模板的类名字(官方称谓:模板名),比如,某些成员的返回值 MyClass getData() { return *this; } 不用写成MyClass<T>。

但是要注意的是类外成员定义中函数返回值类型不在类作用域中,函数体内部在类作用域中。 

 

模板类型别名

  单个模板实例的别名:typedef Blob<string> A;

  类模板的别名: template<typename T> using test = std::pair<T, int>; 使用的时候 test<string>;

 

 

// 利用AI生成
#include <iostream>
#include <vector>

template <typename T>
class Container {
private:
    std::vector<T> data; // 内部使用 vector 模板存储元素,使用了模板参数T

public:
    // 在类内部声明成员函数
    void add(const T& value) {
        data.push_back(value);
    }
    void remove(int index) {
        if (index >= 0 && index < data.size()) {
            data.erase(data.begin() + index);
        }
    }
    void update(int index, const T& value) {
        if (index >= 0 && index < data.size()) {
            data[index] = value;
        }
    }
    T& get(int index) {
        return data[index];
    }
    int getSize() const {
        return data.size();
    }

    // 在类外部定义的成员函数,使用了类模板的类型参数 T
    void print();
};

// 在类外部定义的成员函数,使用了类模板的类型参数 T
template <typename T>
void Container<T>::print() {
    for (const auto& item : data) {
        std::cout << item << " ";
    }
    std::cout << std::endl;
}

int main() {
    // 使用 int 类型的类模板
    Container<int> intContainer;
    intContainer.add(1);
    intContainer.add(2);
    intContainer.add(3);
    std::cout << "Size: " << intContainer.getSize() << std::endl; // 输出 "Size: 3"
    intContainer.print(); // 输出 "1 2 3"

    // 使用 double 类型的类模板
    Container<double> doubleContainer;
    doubleContainer.add(3.14);
    doubleContainer.add(2.718);
    std::cout << "Size: " << doubleContainer.getSize() << std::endl; // 输出 "Size: 2"
    doubleContainer.print(); // 输出 "3.14 2.718"

    return 0;
}

 

posted @ 2024-03-27 22:39  W-cats  阅读(7)  评论(0编辑  收藏  举报