模板进阶

1.非类型模板参数

类型模板参数:

  • template里面是class/typename这种
    template <class T, class Container = std::vector<T>, class Compare = Less<T> > 

    class priority_queue

    {
        。。。。。
    }

举例

  1. 只传参数不赋值
template<size_t N = 10, bool flag = false>
class Stack
{
private:
	int _a[N];
	int _top;
};


int main()
{
	Stack<> s0;
	Stack<5> s1;
	Stack<10> s2;

	return 0;
}

注意:

  • 只用于整形家族:char ,int,long,‼️bool‼这种(c++20后面支持了double)
  • 必须在编译期就能确定结果
  • 给了非类型模板参数 缺省值,不能直接不加<>就创建——>必须要:Stack<> s0;这样写(c++ 后面支持了)





array

其实用vector更好,不过array是栈上,vector是堆上

  • c++的代替C语言数组的一个数据结构
  • 用法:
    • array<int, 100> a1
    • array<double, 10> a2
  • 好处:
    • 越界检查更方便










2.函数模板的特化

不建议用




2.1引出:

下面的代码没有对指针的比较问题

// 函数模板 -- 参数匹配
template<class T>
bool LessFunc(T left, T right)
{
    return left < right;
}

int main()
{
    cout << LessFunc(1, 2) << endl; // 可以比较,结果正确

    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << LessFunc(d1, d2) << endl; // 可以比较,结果正确

    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << LessFunc(p1, p2) << endl; // 可以比较,结果错误❌

    return 0;
}



特化示例:


template<class T>
bool LessFunc(T left, T right)
{
    return left < right;
}

template<>
bool LessFunc<Date*>(Date* left, Date* right)
{
    return *left < *right;
}



更推荐将特化改成函数:

当然特化和函数也可以同时存在,不过还是会调用函数,因为特化还是模板,使用时会实例化,麻烦

template<class T>
bool LessFunc(T left, T right)
{
    return left < right;
}
 

bool LessFunc (Date* left, Date* right)
{
    return *left < *right;
}





2.2格式

template<>
bool LessFunc<Date*>(Date* left, Date* right)
{
    return *left < *right;
}
  1. 必须要先有一个 函数模板
  2. 然后创造一个相似的函数模板,区别如下:
    1. 空的<> : 关键字 template 后面 尖括号 <>,是空的
    2. **再加<>:**函数名后跟一对尖括号,尖括号中指定需要特化的类型
    3. **形参相同:**必须要和模板函数的基础参数类型完全相同





2.3特化的问题




一步一步说:

引出:前面的模板写的不是很好,为防止拷贝构造,应该把T都改成const T &——>

template<class T>
//bool LessFunc(T const & left, T const & right)
bool LessFunc(const T& left, const T& right)
{
	return left < right;
}

但是,特化就对不上了——>改一下特化为:

bool LessFunc (const Date*& left, const Date*& right)
{
    return *left < *right;
}

‼️这时候是错的

原因:

注意看const Date*& left,const是在*前面——>left指向的内容不能改变,而left可变

——>和模板中的:const T& left对不上了

——>应该写成:

bool LessFunc ( Date* const & left,  Date* const & right)
{
    return *left < *right;
}



所以,更推荐写成“和普通函数的重载”











3.类模板的特化

相当于是模板的重载——>就像函数重载一样,类型不同,调用的模板/函数 虽然重名,但不是一个






格式

特化类模板的书写:

  1. 必须要先有一个 类模板
  2. 然后创造一个相似的函数模板,区别如下:
    1. template后的<>中,内容不确定‼️
    2. **class 类名后加<>:**函数名后跟一对尖括号,尖括号中指定需要特化的类型



特化类模板的实例化:

  • 类模板名+尖括号(其中有对应的类型)+类名
    • 例如:Data<int&, double&> d9;






种类

无特化:

template<class T1, class T2>
class Data
{
    
};



全特化:

  • 如果实例化时的两个类型和如下特化模板相同,就调用对应的全特化模板
template<>
class Data<int, char>
{
    
};



偏特化/半特化:

  • 如果实例化时,对应位置的类型符合,就调用对应的偏特化模板
template<class T1>
class Data<T1, double>
{
    
};



传指针的特殊偏特化:

  • 实例化时<>中是指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
    
};



传引用的特殊偏特化:

  • 实例化时<>中是引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
    
};





注意点:

实例化时,<>中的内容,如果传的是*(指针)或&(引用),在偏特化类模板中对应的内容是原类型‼️

⬇️

对于他俩(以及他俩的混合——>第三列)

传指针的特殊偏特化:传引用的特殊偏特化:既有传指针又有传引用
偏特化的写法template <typename T1, typename T2>
class Data <T1*, T2*>
{

};
template <typename T1, typename T2>
class Data <T1&, T2&>
{

};
template <typename T1, typename T2>
class Data <T1&, T2*>
{

};
实例化Data<int*, char*> d7;Data<int&, double&> d9;Data<int&, int*> d10;
T1和T2的实际指代T1是int
T2是char
T1是int
T2是double
T1是int
T2是int

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

// 全特化
template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
};

// 偏特化/半特化
template<class T1>
class Data<T1, double>
{
public:
	Data() { cout << "Data<T1, double>" << endl; }
};

template<class T1>
class Data<T1, char>
{
public:
	Data() { cout << "Data<T1, char>" << endl; }
};

// 偏特化,传的类型是指针
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() 
	{ 
		cout << "Data<T1*, T2*>" << endl;
	}
};

template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data()
	{
		cout << "Data<T1&, T2&>" << endl;
	}
};


template <typename T1, typename T2>
class Data <T1&, T2*>
{
    
};









4.模板声明定义分离问题




为什么模版不建议?

➡️有关 编译链接+预处理 的知识,可以点击这里进行学习⬅️

答:先假设有3个文件:func.h,func.cpp,main.cpp

  • 声明和定义分离(在func.h中声明,func.cpp定义,main.cpp中使用)的情况:
    1. 虽然有声明和定义,但是func.cpp中没有使用➡️没有实例化➡️没有函数生成‼️
    2. main.cpp编译的时候,即使把.h复制进来,也无法找到函数❌
  • 声明、定义部分离(都在func.h中)
    • main.cpp中使用的时候有定义➡️实例化出函数✅

➡️豆包的详细解释,可以点击这里进行学习⬅️






显示实例化

用来实现模板的声明定义分离

写法:

template 
int add<int>(int, int);
  • 直接一个template,后面什么也不加
  • 下面写函数,函数名后面写<>,尖括号里面写类型

**原因:**写这一行强制生成 int 版本,链接就能找到。

使用频率:日常几乎不用,只用来强行实现分离编译。

之前在模板初阶的时候也讲了一个显示实例化,和这个不是一回事,

原来的显示实例化:

  • 调用侧手动告诉编译器 T 是什么。
  • 当编译器无法推导出模板参数类型时必须用:
Add<int>(a1, d1); // 强制T为int
Add<double>(a1, d1); // 强制T为double

template<class T>
T* func1(int n)
{
	return new T[n];
}

int main()
{
    
	double* p1 = func1<double>(10);//当编译器无法推导出模板参数类型时必须用显式实例化

	return 0;
}











Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐