模板的基本使用

模板简介

在 C++中,模板能够定义一种适用于不同类型对象的行为。这听起来有点像宏,但宏不是类型安全的,而模板是类型安全的。

模板函数

函数模板允许定义一个通用的函数,可以用于处理不同类型的参数。

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

int main() {
    int sum1 = add<int>(3, 4);              // 调用add<int>(3, 4)
    double sum2 = add<double>(2.5, 3.7);    // 调用add<double>(2.5, 3.7)

    return 0;
}

add()是一个函数模板,它接受两个相同类型的参数并返回它们的和。使用template <typename T>语法来定义类型参数T,使其表示函数接受的参数类型。在main()函数中,我们通过给定的参数类型显式地调用了add()函数,编译器会根据参数类型推断出适当的模板实例。

隐式推断类型

模板的隐式类型推断是指编译器根据函数调用时给定的参数类型,自动推断出模板参数的具体类型。

例如,考虑以下模板函数:

template<typename T>
void foo(T arg) {
    // ...
}

当我们调用foo函数时,可以省略模板参数的显式指定,而只提供函数参数。编译器会根据该参数的类型来推断模板参数的类型,例如:

foo(42);  // 推断 T 为 int
foo(3.14);  // 推断 T 为 double
foo("hello");  // 推断 T 为 const char*

在这些示例中,编译器会根据调用时的实际参数类型推断出模板参数 T 的具体类型,并生成对应的函数版本。

三种情况不能做隐式推断:

  1. 多个参数的函数模板:

如果函数模板有多个模板参数,编译器可能无法推断所有的参数类型。

template<typename T, typename U>
void foo(T t, U u) { /* ... */ }

// 隐式推断会失败,因为编译器无法确定 T 和 U 的类型
foo(1, 2.0);

  1. 函数模板的返回类型:

编译器无法从函数调用中推断出函数模板的返回类型

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

// 隐式推断无法确定返回类型 T
auto result = add(1, 2);

  1. 显式指定模板参数类型的情况:

如果在函数调用或对象创建时显式指定了模板参数类型,编译器将不会进行隐式推断。例如:

template<typename T>
void func(T t) { /* ... */ }

// 显式指定模板参数类型,不会进行隐式推断
func<int>(10);

模板类

类模板允许定义一个通用的类,可以在其中使用一个或多个类型参数。

template <typename T>
class Pair {
private:
    T first;
    T second;
//这里使用了类型参数 T,表示这两个成员变量的类型可以根据实例化时指定的具体类型来确定。

public:

    Pair(T a, T b) : first(a), second(b) {}
//构造函数对传入的参数对类的成员变量进行初始化。

    T getFirst() const { return first; }
    T getSecond() const { return second; }
};

int main() {
    Pair<int> p1(3, 4);              
	// Pair<int>类型的实例,创建了一个 Pair 类型的对象 p1
	
    Pair<double> p2(2.5, 3.7);       
	// Pair<double>类型的实例

    int first = p1.getFirst();       // 返回3
    double second = p2.getSecond();   // 返回3.7

    return 0;
}

声明包含多个参数的模板

要声明一个包含多个参数的模板,可以使用逗号分隔的形式来指定多个类型参数。

template<typename T1, typename T2>
void myFunction(T1 arg1, T2 arg2) {
    // 函数体
}

myFunction 是一个函数模板,它有两个类型参数 T1 和 T2。可以根据需要添加更多的类型参数,只需在模板参数列表中用逗号分隔即可。

template<typename T1, typename T2>
class MyClass {
private:
    T1 member1;
    T2 member2;

public:
    MyClass(T1 arg1, T2 arg2) : member1(arg1), member2(arg2) {
        // 构造函数体
    }

    // 其他成员函数
};

声明包含默认参数的模板

要声明一个包含默认参数的模板,可以在模板参数列表中为特定的类型参数提供默认值。

template<typename T = int>
void myFunction(T arg) {
    // 函数体
}

上面的代码中,myFunction是一个函数模板,它有一个类型参数T,并将 int 设置为其默认值。如果在使用该模板时未显式提供类型参数,则默认使用 int类型。

声明参数数量可变的模板

要声明一个参数数量可变的模板,可以使用 C++ 中的可变参数模板和递归展开技术。

template<typename... Args>
void myFunction(Args... args) {
    // 对参数进行处理
}

myFunction是一个函数模板,它使用了可变参数模板的语法 typename... Args
这意味着 Args是一个包含零个或多个类型参数的参数包。
在函数体内部,我们可以使用展开符号 ... 来展开参数包 args,以便在处理参数时可以访问每个参数。

#include <iostream>

// 递归终止条件
void printValues() {
    std::cout << std::endl;
}

// 递归展开参数包并打印每个参数的值
template<typename T, typename... Args>
void printValues(T value, Args... args) {
    std::cout << value << " ";
    printValues(args...);
}

int main() {
    printValues(1, 2.5, "Hello", true);
    return 0;
}

printValues函数是一个递归函数模板,用于展开参数包并打印每个参数的值。当没有参数时,递归调用将到达终止条件。

类模板的静态成员

类模板的静态成员既不是每个对象拥有一份,也不是类模板拥有一份,而应该是由类模板实例化出的每一个真正的类各有一份,且为该实例化类定义的所有对象共享

在类外定义静态成员变量并初始化

template<typename T>
class CMath {
public:
    static int s_iValue;
};

// 在类外定义并初始化静态成员变量
template<typename T>
int CMath<T>::s_iValue = 0;

在模板类外定义成员函数

//在模板类声明内部声明成员函数,并提供函数的原型。
template<typename T>
class CMath {
public:
    void print();
};

//在类外部进行成员函数的定义和特化。

template<typename T>
void CMath<T>::print() {
    std::cout << "Printing from template function." << std::endl;
}

// 特例化 int 类型的成员函数
template<>
void CMath<int>::print() {
    std::cout << "Printing from specialized function for int." << std::endl;
}


CMath::print() 格式是用于调用模板类的静态成员函数的,而不是普通类的成员函数。
对于普通类的成员函数,需要通过创建类的对象来调用

类模板的递归实例化

可以使用任何类型来实例化类模板,由类模板实例化产生二点类也可以用来实例化类模板自身,这种做法称之为类模板的递归实例化,通过这种方法可以构建空间上具有递归特性的数据结构

类模板的扩展

类模板的特化是指对于特定的数据类型,为模板参数提供特定的实现。通过特化,可以根据具体的数据类型定制类模板的行为。

全局特化

针对模板的所有参数进行特化

template <typename T, typename U>
class MyClass {
public:
  void print() {
    std::cout << "General Template" << std::endl;
  }
};

template <>
class MyClass<int, double> {
public:
  void print() {
    std::cout << "Specialization for int and double" << std::endl;
  }
};

局部特化

仅对一部分模板参数进行特化。

template <typename T, typename U>
class MyClass {
public:
  void print() {
    std::cout << "General Template" << std::endl;
  }
};

template <typename T>
class MyClass<T, double> {
public:
  void print() {
    std::cout << "Partial Specialization for T and double" << std::endl;
  }
};

使用特化后的类模板时,编译器会根据实际传递的模板参数匹配相应的特化版本或通用版本

MyClass<int, float> obj1;
obj1.print();  // 输出:General Template

MyClass<int, double> obj2;
obj2.print();  // 输出:Specialization for int and double

MyClass<char, double> obj3;
obj3.print();  // 输出:Partial Specialization for T and double

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值