C/C++编程:模板的基本语法

1060 篇文章 307 订阅

Template Class基本语法

基本语法

声明:

template < typename T> class ClassA;

定义:

template < typename T> class ClassA
{
T member;
}

template表示接下来将会定义一个模板。和函数一样,模板也有一系列参数。这些参数都被囊括在template之后的< >中。在上文的例子中,typename T便是模板参数。 来看一个函数声明:

void ff(int a);

typename T可以类比int a, T是形参,typename表示模板参数中的T将匹配一个类型。

我们可以用类似ClassA<int>来实例化类ClassA,那么ClassA<int>等同于:

// 注意:这并不是有效的C++语法,只是为了说明模板的作用
typedef class {
    int member;
} ClassA<int>;

怎么使用

定义一个类模板:

template <typename T>
class vector
{
public:
    void push_back(T const&);
    void clear();				
	
private:
    T* elements;
};

对于C++来说,类型最重要的作用之一就是用它去产生一个变量,因此,可以这样用:

vector<int> intArray;
vector<float> floatArray;

此时我们就可以执行以下的操作,获得我们想要的结果:

intArray.push_back(5);
floatArray.push_back(3.0f);

变量定义的过程可以分成两步来看:第一步,vector<int>将int绑定到模板类vector上,获得了一个“普通的类vector<int>”;第二步通过“vector”定义了一个变量。

我们把通过类型绑定将模板类变成“普通的类”的过程,称之为模板实例化(Template Instantiate)。实例化的语法是:

模板名 < 模板实参1 [,模板实参2...] >

当然,在实例化过程中,被绑定到模板参数上的类型(即模板实参)需要与模板形参正确匹配。 就如同函数一样,如果没有提供足够并匹配的参数,模板便不能正确的实例化。

模板类的成员函数

C++11正式废弃模板导出这一特性,因此在模板类的变量在调用成员函数的时候,需要看到完整的成员函数定义。因此现在的模板类中的成员函数,通常是以内联的方式实现的。

template <typename T>
class vector
{
public:
    void clear()
    {
        // Function body
    }
	
private:
    T* elements;
};

当然,我们也可以将vector::clear的定义部分放在类型之外,只不过这个时候的语法就显得蹩脚许多:

template <typename T>
class vector
{
public:
    void clear();  // 注意这里只有声明
private:
    T* elements;
};

template <typename T>
void vector<T>::clear()  // 函数的实现放在这里
{
    // Function body
}

在成员函数实现的时候,必须要提供模板参数。此外,为什么类型名不是vector而是vector<T>呢? 如果你了解过模板的偏特化与特化的语法,应该能看出,这里的vector在语法上类似于特化/偏特化。实际上,这里的函数定义也确实是成员函数的偏特化

综上,正确的成员函数实现如下所示:

template <typename T> // 模板参数
void vector<T> /*看起来像偏特化*/ ::clear() // 函数的实现放在这里
{
    // Function body
}

Template Function基本语法

template <typename T> void foo(T const& v);

template <typename T> T foo();

template <typename T, typename U> U foo(T const&);

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

使用:

#include <list>
#include <vector>

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



int main()
{
    int a = 5;
    int b = 3;
    int result = Add<int>(a, b);
}

整型也可以是Template参数

模板参数除了类型外(包括基本类型、结构、类类型等),也可以是一个整型数(Integral Number)。这里的整型数比较宽泛,包括布尔型,不同位数、有无符号的整型,甚至包括指针。我们将整型的模板参数和类型作为模板参数来做一个对比:

template <typename T> class TemplateWithType;
template <int      V> class TemplateWithValue;

我想这个时候你也更能理解 typename 的意思了:它相当于是模板参数的“类型”,告诉你 T 是一个 typename。

按照C++ Template最初的想法,模板不就是为了提供一个类型安全,容器调试的宏吗?有类型就够了,为什么要引入整形参数呢?考虑宏,它除了代码替换,还有一个作用是作为常量出现。所以整形模板参数最基本的用途就是定义一个常数。比如:

template <typename T, int Size> struct Array
{
    T data[Size];
};

Array<int, 16> arr;

相当于

class IntArrayWithSize16
{
    int data[16]; // int 替换了 T, 16 替换了 Size
};

IntArrayWithSize16 arr;

另外,因为模板的匹配是在编译时完成的,所以实例化模板的时候所使用的参数,也必须要在编译期就能确定

template <int i> class A {};

void foo()
{
    int x = 3;
    A<5> a; // 正确!
    A<x> b; // error C2971: '_1_3::A' : template parameter 'i' : 'x' : a local variable cannot be used as a non-type argument
}

因为x不是一个编译期常量,所以 A 就会告诉你,x是一个局部变量,不能作为一个模板参数出现。

嗯,这里我们再来写几个相对复杂的例子:

template <int i> class A 
{
public:
    void foo(int)
    {
    }
};
template <uint8_t a, typename b, void* c> class B {};
template <bool, void (*a)()> class C {};
template <void (A<3>::*a)(int)> class D {};

template <int i> int Add(int a)	// 当然也能用于函数模板
{
    return a + i;
}

void foo()
{
    A<5> a;
    B<7, A<5>, nullptr>	b; // 模板参数可以是一个无符号八位整数,可以是模板生成的类;可以是一个指针。
    C<false, &foo> c;      // 模板参数可以是一个bool类型的常量,甚至可以是一个函数指针。
    D<&A<3>::foo> d;       // 丧心病狂啊!它还能是一个成员函数指针!
    int x = Add<3>(5);     // x == 8。因为整型模板参数无法从函数参数获得,所以只能是手工指定啦。
}

template <float a> class E {}; // ERROR: 别闹!早说过只能是整数类型的啦!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值