跟我学c++中级篇——模板的模板参数和成员模板

一、template template parameter和嵌套模板

template template parameter,模板的模板参数,说的简单一些就是模板的形参仍然是模板,可以多层的定义,但一般都是两层;而嵌套模板,一般是指从调用层次上来看,也就是说模板调用模板。下面看一些简单的定义说明:

//template template parameter

  template<typename T>
  struct Data 
 {
   ...... 
 };
  
  template<template<typename T> class MyData>
  constexpr auto Test(MyData<int> md) 
 {
   ......
 }

 Test(Data<int>{});
//模板嵌套
  template<typename T>
  struct A
 {
   ...... 
 };
  template<typename T>
  struct B
 {
   ...... 
 };

 auto c = B<A>(); 
  

嵌套并不是官方术语,只是有一些开发者的称谓,所以这里不必纠结。可能理解嵌套为template template parameter也可以。

二、template template parameter应用

如果看过STL的源码,会发现在STL中有很多这种用法。同样学习STL中的用法,创建一个容器类,使用模板的模板参数,可以动态指定容器的类型为vector或者queue等。下面看一个实际的代码:

template<typename T,template <typename T> class Container>
class XCls
{
private:
Container<T> c;
......
};

这是侯捷老师在视频中的代码。但是他又认为类似下面的代码不是template template parameter:

template <typename T, typename Cont = std::vector<T>> 
class Stack {
private:
  Cont elems; // elements
  ......
};

侯捷老师认为其应用时有两种情况,一种是使用默认的参数,只有第一个形参起作用;第二种是更换后面的Cont,则形参定义完全指定(list)(第一种其实也是),就没有了模板做参数的那种参数未定义的模糊状态(只传入list)。
而只需要从定义的形式上来看,Cont只是一个普通形参而不是模板做为形参,这样理解可能更简单一些。但是不是完全准确,需要大家来讨论。
需要注意的是在以前版本模板参数需要使用class,c++17后才允许使用typename。所以在不同的编译器环境下如果有问题要注意修正。

三、外部定义的成员模板

在一些开源的框架代码中可以到到类似下面的代码:

// member  templates  outside class
template<typename T>
class A
{
public:
   template<typename S>
   void test(const S &s);
};

template<typename T> 
template <typename S>
void A<T>::test(const S &s)
{
}

在编写普通类的时候儿,就会有几种写法,一种是头文件和cpp文件分开,头文件只管定义(除了内联函数和一些简单函数),而真正的函数定义在cpp文件中。另外一种是全部写在头文件中。而全部写在头文件中又有两种情况,一种是把函数定义和函数实现分开;一种是类中函数的声明和定义写在一起。
而模板不支持分离编译,那么只有在头文件中的一种情况,即分开定义或者在类中定义和声明在一起。上面的就是前者,也叫做模板成员函数的外部定义(Define member templates outside class)。另外还有一种情况当然就是定义在一起的,就比较常见了。其它诸如非模板类中定义函数模板以及其它相关用法就没有什么特别的了。
另外注意一下这种定义的形式:

template<typename T>
template<typename T1>
...

定义于Class外的成员模板,可以有多重的template<…>参数子句,可以将其当成竹笋一样,从外向内,一层层的向内伸展,最里的代表着实际的成员template本身.

四、例程

下面看一个模板的模板参数的例程:

#include <iostream>

template<typename T>
class A
{
public:
	A() { std::cout << "A Constructor!" << std::endl; }
};
template<typename T>
class B
{
public:
	B() { std::cout << "B Constructor!" << std::endl; }
};
//此处的S可以省略
template<typename T, template<typename S> class Ex>
class C
{
public:
	C() { std::cout << "C Constructor!" << std::endl; }
public:
	Ex<int> ex;
};
//此处与侯捷老师所说的细节比较一下
template<typename T, template<typename > class Ex = A>
class D
{
public:
	D() { std::cout << "D Constructor!" << std::endl; }
public:
	Ex<int> ex;
};
int main()
{
	C<double, A> c;	
	D<double> d1;
        D<double, B> d2;
}

把这个运行一下,看一个构造函数的调用打印顺序,自然就明白一切了。

五、总结

之所以把template template parameter和成员模板放在一起,主要是有些东西猛的看上去比较奇怪,特别是对新手而言,可能一时不好翻书翻到。现在把这两个放在一起,一下子就可以引起注意,并且不容易造成二者的混淆。
俗话说的好,“拳不离手,曲不离口”。技术也是如此,不常用慢慢就淡忘了。

猜你喜欢

转载自blog.csdn.net/fpcc/article/details/129231553