c++模板进阶

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析3

在这里插入图片描述


👉🏻非类型模板参数

在 C++ 中,模板的非类型模板参数是指一些非类型值,在模板实例化时作为模板实参进行传递。它们可以是整型、浮点型、枚举类型、指针、引用等常见类型,但不能是所有类型都可以。

非类型模板参数必须在编译时就确定其值,并且定义时需要显示指定初始值。它们可以用于编译时计算和处理数据,进而实现更加泛化和高效的代码。下面是一些示例:

  1. 定义非类型模板参数(整型):
template <int N>
class MyClass {
public:
    void print() {
        cout << "N = " << N << endl;
    }
};

// 实例化一个类
MyClass<10> obj;
obj.print();  // 输出 N = 10
  1. 定义非类型模板参数(枚举类型):
enum class Color { RED, GREEN, BLUE };

template <Color c>
class MyClass {
public:
    void print() {
        cout << "Color = " << static_cast<int>(c) << endl;
    }
};

// 实例化一个类
MyClass<Color::RED> obj;
obj.print();  // 输出 Color = 0
  1. 定义非类型模板参数(指针类型):
template <int* P>
class MyClass {
public:
    void print() {
       cout << "*P = " << *P << endl;
    }
};

int x = 10;
int* ptr = &x;

// 实例化一个类
MyClass<&x> obj;
obj.print();  // 输出 *P = 10

需要注意的是,对于非类型模板参数,它们必须在编译时就被确定其值。这也意味着对于指针类型的非类型模板参数,其值需要在编译时就确定,而不是在运行时才能确定,说人话就是传的参数必须是常量

总之,C++ 的非类型模板参数是一种强大的特性,可以使代码更加泛化和高效,但同时也需要谨慎使用。在实际编码中应该注意非类型模板参数的限制条件,并根据具体情况合理使用。

👉🏻类模板的特化

C++ 模板的类模板特化是指对于特定类型或模板参数组合,提供一个专门实现的模板版本。类模板特化可以根据特定的需求对通用的模板进行特殊处理,以满足特定类型或模板参数的要求。

类模板特化有两种形式:完全特化(full specialization)和偏特化(partial specialization)。

  1. 完全特化(Full Specialization):
    完全特化是指为特定的类型或模板参数组合提供一个单独的模板定义。完全特化的模板定义会覆盖原始的类模板定义,针对特定的类型或模板参数进行定制化的实现。

下面是一个示例,演示了类模板 MyClass 的完全特化:

// 原始的类模板定义
template <typename T>
class MyClass {
public:
    void print() {
        cout << "Generic version" << endl;
    }
};

// 类模板的完全特化定义
template <>
class MyClass<int> {
public:
    void print() {
        cout << "Specialized version for int" << endl;
    }
};

int main() {
    MyClass<double> obj1;
    obj1.print();       // 输出 "Generic version"

    MyClass<int> obj2;
    obj2.print();       // 输出 "Specialized version for int"

    return 0;
}

在上述示例中,通过 template <>MyClass<int> 进行了完全特化,重写了 print() 函数的实现。当使用 MyClass<int> 类型实例化对象时,会使用完全特化的定义。

  1. 偏特化(Partial Specialization):
    偏特化是指对模板参数中的一部分进行特化,而保持另一部分为通用定义。通过偏特化,可以根据模板参数的某些属性选择不同的实现方式。

以下示例演示了类模板 Pair 的偏特化:

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

// 类模板的偏特化定义
template <typename T>
class Pair<int ,T> {
public:
	Pair(int first,T second) : first(first), second(second)
    {}
	void print() {
		cout << "Partial specialization for second as int: " << first << ", " << second << endl;
	}
private:
    int first;
	T second;
	
};

int main() {
	Pair<double, string> obj1(3.14, "hello");
	obj1.print();       // 输出 "Generic version: 3.14, hello"

	Pair<int, string> obj2(42, "world");
	obj2.print();       // 输出 "Partial specialization for second as int: 42, world"

	return 0;
}

在上述示例中,通过 template <typename T> 中的类型参数 Ttemplate <typename T, typename U> 中的类型参数 U,对 Pair 进行了偏特化。当第一个模板参数为 int 时,使用偏特化的定义。

通过类模板的特化,可以根据特定的数据类型或模板参数组合,为类模板提供专门的实现方式,增强了其灵活性和适用性。使用类模板的特化可以更好地应对复杂的应用场景和需求。

👉🏻 模板的分离编译

在c++中,不建议将模板的声明和定义分离

在 C++ 中,模板函数的声明与定义通常需要放在同一个文件中,不可以分离。这是因为 C++ 的编译模型决定了模板函数的实现必须对编译器可见。

C++ 的编译模型大致可分为两个步骤:编译和链接编译器在编译阶段会根据使用到的函数或类的声明生成相应的代码,但模板函数的实例化需要根据具体的模板参数推导来进行,在编译阶段无法完成实例化过程像普通函数在声明那都可以完成实例化)。而模板函数的定义包含了实例化的具体代码,因此,编译器无法在没有定义的情况下对模板函数进行实例化。

如果将模板函数的声明和定义分离在不同的文件中,编译器在编译声明所在的文件时无法找到对应的定义,从而无法进行实例化操作,会导致链接阶段出现符号未定义的错误

为了解决这一问题,一种常见的做法是将模板函数的声明和定义都放在同一个头文件中(一般文件后缀名为.hpp,声明+定义),并在需要使用模板函数的源文件中包含该头文件。这样可以确保在编译阶段就能够让编译器看到模板函数的定义,从而完成实例化操作。

另外,对于某些特定的模板函数,也可以使用显式实例化的方式,在源文件中显式地实例化模板函数的特定模板参数类型,从而解决分离声明和定义的问题。但这种方式需要在每个使用到模板函数的源文件中都进行实例化操作,比较繁琐,不太常用

总结起来,为了确保模板函数的正确实例化,需要将其声明和定义放在同一个文件中,或者采用显式实例化的方式。这样可以避免编译阶段缺少定义而导致的链接错误。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值