240604 模板进阶

模板进阶

1. 类模板的非类型模板参数

模板参数分为:
(1) 类型形参:出现在模板参数列表中,跟在class或typename之类的参数类型名称
(2)非类型形参:用一个常量作为类(函数)模板的一个参数,在模板中可将其当作常量使用

假设我们想要创建一个具有固定大小的栈,可以使用非类型模板参数来指定栈的最大尺寸:

#include <array>

template <typename T, std::size_t Maxsize>
class Stack {
private:
    std::array<T, Maxsize> elems; // 元素
    std::size_t numElems;         // 当前元素数量

public:
    Stack() : numElems(0) {}

    void push(T const &elem); // 压入元素
    void pop();               // 弹出元素
    T const &top() const;     // 返回顶部元素
    bool empty() const { return numElems == 0; } // 是否为空
    std::size_t size() const { return numElems; } // 当前元素数量
};

// 实现 push 方法
template <typename T, std::size_t Maxsize>
void Stack<T, Maxsize>::push(T const &elem) {
    assert(numElems < Maxsize);
    elems[numElems] = elem;
    ++numElems;
}

类模板没有实例化时,编译器不会去里面查细节,无法确定里面是类型还是静态变量
typename明确告诉是类型

template<class T>
void PrintVector(const vector<T>& v) {
	typename vector<T>::const_iterator it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

或者直接auto it = v.begin()

2. 函数模板的专用化/特化

仅作了解,不太实用

原模板:

template<class T>
bool Less(const T& l, const T& r){
	return l < r;
}

以日期类举例,原模板专用化后:

template<>
bool Less<Date*>(Date* const& l, Date* const& r) {
    return *l < *r;
}

其他类型都正常走原模板,Date* 走专用化的模板
坑多,一般不建议使用函数模板的专用化

推荐做法

template<class T>
bool Less(T l, T r){
	return l < r;
}

bool Less(Date* l, Date* r){
	return *l < *r;
}

3. 类模板的特化

如果不使用函数模板的特化,更推荐的做法是使用函数重载来处理不同类型的情况。函数重载比模板特化更直观和灵活,在类型明确的情况下可以更好地表达意图。可以通过为特定类型(如Date*)编写专门的重载函数,而不需要对模板进行专门化。
(1)全特化

template<class T1, class T2>
class Show {
public:
	Show() {
		cout << "Show<T1, T2>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};
// 类模板的特化:
template<>
class Show<int, char> {
public:
	Show() {
		cout << "Show<int, char>" << endl;
	}
};

(2)部分特化示例1

// 偏特化:特化部分参数
template<class T1>
class Show<T1, int> {
public:
	Show() {
		cout << "Show<T1, int>" << endl;
	}
private:
	T1 _a;
	int _b;
};

// 偏特化:限定模板类型
// 涉及指针走下面,T1和T2对应的是原类型(不是指针)
template<typename T1, typename T2>
class Show<T1*, T2*> {
public:
	Show() {
		cout << "Show<T1*, T2*>" << endl;
	}
};

(3)部分特化示例2
实现自定义的比较器,用于指针类型之间的比较。
通常用于需要比较两个对象(通过指针)的场合:

template<class T>
class Less<T*>{
public:
	bool operator()(T* const& x, T* const& y){
		return *x< *y;
	}
}

在不同的情况下,T 就是指针所指向的具体类型。例如:
Date* 对应 T = Date
int* 对应 T = int
std::string* 对应 T = std::string

部分特化/偏特化 应用较多

4. 模板分离编译的问题

在C++中,当你在头文件中声明一个模板函数,并在源文件(.cpp文件)中实现它时,可能会遇到模板函数无法成功调用的问题。以下是原因及其解释:

(1)原因分析

  • 模板的实例化要求:模板函数需要在调用时能够看到其完整的定义。由于模板是基于类型参数生成的,每次使用模板时,编译器都需要知道模板的具体实现。如果实现放在 .cpp 文件中,而没有显式地让编译器知道该模板的定义,就会导致编译错误。

  • 普通函数的处理:普通函数(非模板函数)的定义在 .cpp 文件中,编译器在链接时能够找到它的实现,因此可以被成功调用。

(2)示例代码

假设有以下的文件结构:

func.h

#ifndef FUNC_H
#define FUNC_H

template <typename T>
void templateFunction(T value);  // 模板函数的声明

void regularFunction();  // 普通函数的声明

#endif

func.cpp

#include "func.h"
#include <iostream>

template <typename T>
void templateFunction(T value) {  // 模板函数的实现
    std::cout << "Template value: " << value << std::endl;
}

void regularFunction() {  // 普通函数的实现
    std::cout << "Regular function called." << std::endl;
}

// 显式实例化
template void templateFunction<int>(int);  // 显式实例化模板

test.cpp

#include "func.h"

int main() {
    regularFunction();  // 调用普通函数
    templateFunction(10);  // 调用模板函数
    return 0;
}

(3)解决方案

要使模板函数在 test.cpp 中成功调用,通常有以下几种方法:

  • 将模板函数的实现放在头文件中
    templateFunction 的实现直接放在 func.h 文件中,这样每次包含 func.h 时都会可见。

    template <typename T>
    void templateFunction(T value) {  // 模板函数的实现
        std::cout << "Template value: " << value << std::endl;
    }
    
  • 显式实例化很被动,需要不断添加显式实例,具有局限性,不方便
    func.cpp 中使用显式实例化声明,例如 template void templateFunction<int>(int);。这样,编译器在编译 func.cpp 时会生成针对特定类型的代码。

  • 在调用前包含实现
    通过在 test.cpp 中包含 func.cpp,确保模板的实现可用。但这不是推荐的做法,因为它会导致代码的重复编译。

(4)总结

  • 模板函数必须在调用时是可见的。为了避免这种问题,建议将模板的实现放在头文件中,或者使用显式实例化。
  • 普通函数因为有具体的实现和链接,因此在 test.cpp 中可以直接调用。

优点:本质上是把活交给编译器
a. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。
b. 增强了代码的灵活性。
缺点
a. 模板会导致代码膨胀问题,也会导致编译时间变长。
b. 出现模板编译错误时,错误信息非常凌乱,不易定位错误。

<think>好的,我现在需要处理用户关于模板高级用法及案例的查询。首先,用户的问题是关于模板进阶使用,所以我要先理解他们可能想了解哪些方面。模板的高级用法可能包括特化、偏特化、可变参数模板模板元编程、CRTP等。案例方面可能需要具体的代码示例和应用场景。 接下来,我需要调用网络搜索工具来查找相关资料。可能的搜索关键词包括“C++模板高级用法”、“模板特化与偏特化案例”、“可变参数模板应用”、“模板元编程实例”、“CRTP设计模式”等。确保覆盖不同的编程语言,但根据常见情况,可能主要针对C++,因为模板C++中非常强大和复杂。 然后,整合检索到的信息。例如,发现可变参数模板用于实现接受任意数量参数的函数,如printf的替代方案;模板特化用于针对特定类型优化实现;CRTP用于静态多态。需要将这些概念用清晰的方式解释,并提供代码示例。 同时,需要注意用户的要求:分点说明,避免使用步骤词汇,代码和公式格式正确。每个方法或案例之间换行,不使用第一人称。还要确保引用内容分散在回答中,而不是集中在末尾。 现在,检查是否有遗漏的高级概念,比如SFINAE、概念(C++20)、模板模板参数等。可能需要简要提及这些,但根据用户的问题,可能更关注用法和案例,而不是最新标准。不过,概念可以作为一个高级用法提到。 然后,组织回答结构。可能分为可变参数模板、特化与偏特化、模板元编程、CRTP等部分。每个部分给出定义、用途和示例代码。确保代码格式正确,使用三个反引号注明语言类型,数学公式用$包裹。 最后,生成相关问题,需要基于回答内容,比如询问模板元编程的具体应用、CRTP与虚函数的对比、C++20概念如何简化模板等。确保至少三个问题,符合格式要求。 检查所有要求是否满足:分点、换行、代码格式、相关问题部分正确放置等。避免任何第一人称和步骤词汇,确保引用分散。现在可以整理回答并生成最终内容。</think>通过多平台技术文档和开源项目代码分析,模板系统的高级应用主要涉及以下核心方法和典型案例: **类型萃取与编译期计算** ```cpp template<typename T> struct is_pointer { static const bool value = false; }; template<typename T> struct is_pointer<T*> { static const bool value = true; }; ``` 通过模板特化实现类型特征检测,$is_pointer<T>::value$ 在编译期即可确定类型是否为指针 **可变参数模板** ```cpp template<typename... Args> void log(Args&&... args) { (std::cout << ... << args) << '\n'; } ``` 利用折叠表达式处理不定数量参数,支持$log("Error:", errCode, "at", __LINE__)$形式调用 **策略注入模式** ```cpp template<typename Policy> class Widget : private Policy { public: void execute() { Policy::preProcess(); // 核心逻辑 Policy::postProcess(); } }; ``` 通过模板参数注入算法策略,实现编译期策略组合 **表达式模板优化** ```cpp template<typename L, typename R> struct VecSum { L const& l; R const& r; auto operator[](int i) const { return l[i] + r[i]; } }; Vec operator+(Vec const& a, Vec const& b) { return VecSum<Vec, Vec>(a, b); } ``` 延迟计算实现向量运算优化,避免中间变量创建 **类型擦除技术** ```cpp template<typename T> class AnyType { struct Concept { virtual ~Concept() = default; virtual void process() = 0; }; template<typename U> struct Model : Concept { U data; void process() override { /* 使用data */ } }; std::unique_ptr<Concept> ptr; public: template<typename U> AnyType(U&& u) : ptr(std::make_unique<Model<U>>(std::forward<U>(u))) {} }; ``` 通过模板实现运行时多态,支持任意类型存储
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值