模板基础知识3——《C++程序设计语言(第四版)》第25章 特例化 笔记

模板和实参

类型作为实参

一个类型必须在作用域内且可访问,才能作为模板实参

class X{
    class M {/*...*/};
    //...
    void mf();
};

void f()
{
    struct S {/*...*/};
    vector vs; //正确
    vector vm; //错误:X::M是私有的
}

void M::mf()
{
    vector vs; //错误:作用域中没有类型S
    vector vm; //正确
};

值作为实参

template<typename T, int max>
class Buffer {
    T v[max];
public:
    Buffer() { }
    //...
};

Buffer<char, 128> cbuf;
Buffer<int, 5000> ibuf;
Buffer8> rbuf;

传递给模板值参数的实参可以是
1. 整型常量表达式
2. 外部链接的对象或函数的指针或引用
3. 指向非重载成员的指针
4. 空指针

template<typename T, char* label>
class X {
    //...
};

X<int, "BMW323Ci"> x1; //错误:字符串字面值常量作为实参
char lx2[] = "BMW323Ci";
X<int, lx2> x2; //正确:lx2有外部链接

整型模板实参必须是一个常量

constexpr int max = 200;

void f(int i)
{
    Buffer<int, i> bx; //错误
    Buffer<int, max> bm; //正确
}

值模板参数在模板内部是一个常量,无法修改其值

template<typename T, int max>
class Buffer {
    T v[max];
public:
    Buffer(int i) { max = i }; //错误:试图为模板值参数赋值
    //...
};

在模板参数列表中,一个类型模板参数出现后即可当做一个类型来使用,如:

template<typename T, T default_value>
class Vec {
    //...
};

Vec<int, 42> c1;
Vec<int> c11; //默认值是int{},即0
Vec<string, "fortytwo"> c2;
Vec<string> c22; //默认值为string{}, 即""

操作作为实参

map的第一种方案(传递一个特定类型的比较对象),如:

template<typename Key, typename V, bool(*cmp)(const Key &, const Key &)>
class map {
public:
    map();
    //...   
};

bool insensitive(const string &x, const string &y)
{
    //...
}

map<string, int, insensitive> m;

这种方式不是很灵活。特别是map的设计者必须决定使用一个函数指针比较(未知的)Key类型,还是使用某种特定类型的函数对象进行比较。而且,由于比较对象的实参类型必须依赖Key类型,它很难提供默认的比较准则。第二种方案(将比较类型作为一个模板类型参数传递)更常用,也是标准库中所采用的方式。例如:

template<typename Key, Class V, typename Compare = std::less>
class map {
public:
    map() {/*...*/} //使用默认比较
    map(Compare c) : cmp{c} {/*...*/}  //覆盖默认构造函数
    //...
    Compare cmp{}; //默认比较
};

map<string, int> //使用默认比较操作less
map<string, int, std::greater<string>> m2; //用greater()进行比较

lambda不能转换为函数对象类型

using Cmp = bool(*)(const string&, const string&);
map<string, int, Cmp> c3{[](const string &x, const string &y) const{ return x < y; }}; //错误
auto cmp = [](const string &x, const string &y) const{ return x < y; };
map<string, int, decltype(cmp)> c4 {cmp}; //正确

模板作为实参

template<typename T, template<typename> class C>
class Xrefd {
    C mems;
    C refs;
    //...
};

template<typename T>
    using My_vec = vector; //使用默认分配器

Xrefd x1;

template<typename T>
class My_container {
    //...
};

Xrefd x2;

默认模板实参

template<typename T1 = int, typename T2>
class X1 { //错误:默认实参不在尾部
    //...
};

template<typename T1 = int, typename T2 = double>
class X2 { //正确
    //...
};

X2<,float> v1; //语法错误
X2<float> v2; //正确:X2

也可用于函数模板

template<typename Target = string, typename Source = string>
Target to(Source arg)
{
    stringstream interpreter;
    Target result;

    if(!(interpreter << arg))
        ||!(interpreter >> result)
        ||!(interpreter >> std::ws).eof())
        throw runtime_error{"to<>() failed"};

    return result;
}

auto x1 = to<string, double>(1.2); //太明确、太繁琐
auto x2 = to<string>(1.2) //Source被推断为double
auto x3 = to<>(1.2) //Target默认为string;Source被推断为double
auto x4 = to(1.2); //<>是冗余的

特例化

编译器能根据用户使用模板时提供的实参来选择使用哪个定义,称为用户自定义特例化,或简称用户特例化,如:

template<typename T>
class Vector {
    T* v;
    int sz;
public:
    Vector();
    explicit Vector(int);

    T& elem(int i) { return v[i]; }
    T& operator[](int i);

    void swap(Vector&);
    //...
};

Vector<int> vi;
Vector vps;
Vector<string> vs;
Vector<char*> vpc;
Vector ;

完整特例化举例如下:

template<>
class Vector<void*> {
    void** p;
    //...
    void* &operator[](int i);
};

部分特例化举例如下:

template<>
class Vector<void*> {
    void** p;
    //...
    void* &operator[](int i);
};

template<typename T>
class Vector : private Vector<void*>{
public:
    using Base = Vector<void*>;

    Vector(){}
    explicit Vector(int i) : Base(i){}

    T*& elem(int i) { return reinterpret_cast(Base::elem[i]); }
    T*& operator[](int i) { return reinterpret_cast(Base::operator[](i)); };   
    //...
}

Vector vps; //T是Shape
Vector<int**> vppi; //T是int*

接口特例化

有时,特例化并非是为了优化算法,而是为了修改接口乃至表示。complex的通用模板如下:

template<typename>
class complex {
public:
    complex(const T& re = T{}, const T& im = T{});
    complex(const complex&);
    template<typename X>
        complex(const complex&);

    complex& operator=(const complex&);
    complex& operator=(const T&);
    complex& operator+=(const T&);
    //...
    template<typename X>
        complex& operator=(const complex&);
    template<typename X>
        complex& operator+=(const complex&);
}

引用实参对于float效率不高,complex采用传值参数

template<>
class complex<float> {
public:
    //...
    complex<float> &operator=(float);
    complex<float> &operator+=(float);
    //...
    complex<float> &operator=(const complex<float>&);
}
实现特例化
template<typename T, int N>
class Matrix; //T的N维矩阵

template<typename T, 0>
class Matrix {
    T val;
    //...
};

template<typename T, 1>
class Matrix {
    T *elem;
    int sz;
    //...
};

template<typename T, 2>
class Matrix {
    T *elem;
    int dim1;
    int dim2;
    //...
};

主模板

主模板必须在任何特例化版本之前声明
必须在每个特例化版本中都复制约束检查
如果用户特例化了一个模板,则在该版本每次使用时(使用了特例化所用类型),其定义都必须在相同作用域中,即,使用时定义必须是可见的,如:

template<typename T>
class List {
    //...
};
List<int*> li;

template<typename T>
class List { //错误:特例化未定义即使用
    //...
};

特例化顺序

一个特例化版本比另一个版本更特殊是指与其特例化模式匹配的所有实参列表也都与另一个版本的特例化模板匹配

函数模板特例化

特例化与重载

对于less的一个针对const char*的特例化版本可编写如下:

template<>
bool less<const char *>(const char *a, const char *b)
{
    return strcmp(a, b) < 0;
}
非重载的特例化

函数特例化:

template<> constexpr int max_value<int>() { return INT_MAX; }
template<> constexpr char max_value<char>() { return CHAR_MAX; }
//...

template<typename Iter>
Iter my_algo(Iter p)
{
    auto x = max_value>();
}

函数重载:

int max2(int) { return INT_MAX; }
char max2(char) { return CHAR_MAX; }

template<typename Iter>
Iter my_algo2(Iter p)
{
    auto x = max2(Vlaue_type{});
    //...
}

你可能感兴趣的:(C++,c++)