16.1 Define a Template(定义一个模板)

OOP能处理类型在程序运行之前都未知的情况,而在泛型编程中,在编译时就能获知类型了。

 

template <模板形参列表>
//模板形参列表:模板形参1,模板形参2...

 

在模板定义中,模板参数列表不能为空。

 

模板的实例:在调用模板时,形参被替换为具体类型过程。

 

#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <typename T>
int compare(const T& a, const T& b) {
    return a > b ? 1 : (a == b ? 0 : -1);
}

int main()
{
    vector<int> vec1{ 1, 2, 3, 4 }, vec2{ 1, 2, 3, 4, 5 };
    string str1("axaa"), str2("asda");
    switch (compare(str1, str2)) {
        case 1:
            cout << "大于" << endl;
            break;
        case 0:
            cout << "相等" << endl;
            break;
        case -1:
            cout << "小于" << endl;
            break;
    }
    system("PAUSE");
    return 0;
}

 

 

类型参数前必须使用关键字class或typename。

 

#include <iostream>
#include <string>

using namespace std;

template <unsigned T, unsigned U>
int compare(const char(&p)[T], const char(&q)[U]) {
    return strcmp(p, q);
}

int main()
{
    cout << compare("qbiu", "giu") << endl;//[5],[4]

    system("PAUSE");
    return 0;
}

 

一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用。绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。我们不能用一个普通(非static)局部变量或动态对象作为指针或引用非类型模板参数的实参。指针参数也可以用nullptr或一个值为0的常量表达式来实例化。


在模板定义内,模板非类型参数是一个常量值。

 

inline或constexpr说明符放在模板参数列表之后,返回类型之前。

template <typename T> inline T min(const T&, const T&);

 

编写泛型代码的两个重要原则:

  • 模板中的函数参数是const的引用。
  • 函数体中的条件判断仪使用<比较运算。

模板程序应该尽量减少对实参类型的要求。

 

类模板与函数模板的不同之处是,编译器不能为类模板推断模板参数类型。

 

与类不同,我们的模板可以用于更多类型的元素。

 

当使用一个类模板时,我们必须提供额外信息。我们现在知道这些额外信息是显式模板实参列表,它们被绑定到模板参数。

 

当编译器从我们的Blob模板实例化出一个类时,它会重写BIob模板,将模板参数T的每个实例替换为给定的模板实参。

 

与其他任何类相同,我们既可以在类模板内部,也可以在类模板外部为其定义成员函数,且定义在类模板内的成员函数被隐式声明为内联函数

 

默认情况下,对于一个实例化了的类模板,其成员只有在使用时才被实例化。

 

当我们处于一个类模板的作用域中时,编译器处理模板自身引用时就好像我们已经提供了与模板参数匹配的实参一样。

 

在一个类模板的作用域内,我们可以直接使用模板名而不必指定模板实参.

 

非重载的友元函数,需要前置声明。

#include <iostream>
#include <string>

using namespace std;

template <typename> class BlobPtr;
template <typename> class Blob;

template <typename U>
bool operator==(const Blob<U>&, const Blob<U>&);//前置声明

template <typename U> class Blob {
    friend class BlobPtr<U>;
    friend bool operator==<U>(const Blob<U>&, const Blob<U>&);
};

template <typename T> class BlobPtr {
public:
    BlobPtr() = default;
    BlobPtr<T> operator++(int);
};

template <typename T>
BlobPtr<T> BlobPtr<T>::operator++(int){
    BlobPtr ret = *this;
    ++*this;
    return ret;
}

int main()
{

    system("PAUSE");
    return 0;
}

 

#include <iostream>
using namespace std;


//非重载的友元函数,需要前置声明
template <unsigned length, unsigned width> class Screen {
public:
    void print() { cout << L << "*" << W << "=" << L * W << endl; }
    Screen() { L = length; W = width; };
    Screen(unsigned, unsigned);
    friend ostream& operator<<<length, width>(ostream&,const Screen<length, width>&);
private:
    unsigned L;
    unsigned W;
};

template <unsigned length, unsigned width>
Screen<length, width>::Screen(unsigned len, unsigned wid) :L(len), W(wid) {}

template <unsigned length, unsigned width>
ostream& operator<<(ostream& ou,const Screen<length, width>& A) {
    ou << "面积为:" << A.L * A.W << endl;
    return ou;
}

 

新标准允许我们为类模板定义一个类型别名。

#include <iostream>
#include <string>


using namespace std;

template<typename T> using twin = pair<T, T>;

int main()
{
    twin<string> val1 = { "hello", "you" };
    twin<int> val2(5,9);

    cout << val1.first << endl;
    cout << val2.second << endl;
    system("PAUSE");
    return 0;
}

 

类模板的static成员

template <typename T> class A{
public:
    static std::size_t count(){ return ctr; }
private:
    static std::size_t ctr;
};

定义和初始化ctr

template <typename T>
std::size_t A<int>::ctr = 0;

调用static方式

A<int> a;

auto ct = A<int>::count();
ct = a.count();

 

函数模板:函数模板是可以实例化出特定函数的模板。

类模板:类模板是可以实例化出特定类的模板。

 

在类模板中的作用域中可以不提供实参,直接使用模板名。

 

一个模板参数名的可用范围是在其声明之后,至模板声明或定义结束之前。与任何其他名字一样,模板参数会隐藏外层作用域中声明的相同。

 

一个模板参数名在一个特定模板参数列表中只能出现一次。

 

模板声明必须包含模板参数。

 

当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class.

与函数默认实参一样,对于一个模板参数,只有当它右侧的所有参数都有默认实参时,它才可以有默认实参。

 

#include <iostream>
#include <string>
#include <vector>

using namespace std;

template <typename T, size_t n>
const T* arr_begin(const T (&arr)[n]) {
    return &arr[0];
}

template <typename U, size_t n>
const U* arr_end(const U(&arr)[n]) {
    return &arr[n-1];
}

int main()
{
    int arr[] = { 45, 5, 78, 11 };
    cout << *arr_begin(arr) << endl;
    cout << *arr_end(arr) << endl;
    system("PAUSE");
    return 0;
}

 

与类模板的普通函数成员不同,成员模板是函数模板。当我们在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表。类模板的参数列表在前,后跟成员自己的模板参数列表。

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class DebugDelete {
public:
    DebugDelete(ostream &os = cerr):ou(os){}
    template <typename T> void operator()(T* pt) {
        ou << "Delete the new zone." << endl;
        delete pt;
    }
private:
    ostream &ou;
};

int main()
{
    double* p = new double;
    DebugDelete d;
    d(p);
    //d.operator()(p);
    //DebugDelete()(p);
    shared_ptr<int> pt(new int(45));
    cout << pt << endl;
    cout << pt.get() << endl;


    system("PAUSE");
    return 0;
}

 

对每个实例化声明,在程序中某个位置必须有其显式的实例化定义。

#include <iostream>
#include <string>
#include <vector>
#include "F.cpp"

using namespace std;

template class A<int>;

int main()
{
    A<int> a(5);
    a.func();
    A<int> b(10);
    b.func();
    A<int> c(10);
    c.func();
    A<int> d(10);
    d.func();

    system("PAUSE");
    return 0;
}

 

F.cpp

#include <iostream>
#include <string>
#include <vector>

using namespace std;

template <typename T>class A {
public:
    void func() {
        cout << "Success!!!---" << a << endl;
    }
    A(T s):a(s){}
private:
    T a;
};

extern template class A<int>;//使用了之后节约0.1M内存
posted @ 2019-02-16 16:12  Hk_Mayfly  阅读(355)  评论(0)    收藏  举报