C++11(15): 模板与泛型编程

面向对象编程和泛型编程都能处理在编写程序时不知道类型的情况。不同之处:OOP能处理类型在程序运行之前都未知的情况;而泛型编程中,在编译时就能获知类型了
模板参数类别不能为空。
模板参数表示在类或函数定义中用到的类型或值。
template <typename T>
int compare(const T &v1 , const T &v2)
{
    if(v1<v2) return -1;
    if(v2<v1) return 1;
    return 0 ;
}
cout<<compare(1,0) <<endl;//T为int
编译器生成的版本通常被称为模板的实例。

特别是,类型参数可以用来指导返回类型或函数参数类型,以及在函数内用于变量声明或类型转换:
类型参数前必须加上class或typename(typename是在模板已经广泛使用之后才引入c++语言的)
非类型行参数必须是常量表达式:template<unsigned N, unsigned M> 
当一个模板实例化时,非类型参数被一个用户提供的或编译器推导出来的值所代替。
一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用。绑定到非类型参数的实参时一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。指针参数也可以用nullptr或一个值为0的常量表达式来实例化。

函数模板可以声明成inline或constexptr
template<typename T> inline T min(const T&, const T&);

compare函数说明了编写泛型代码的两个重要原则:
    模板中的函数参数是const的引用。
    函数体中的条件判断仅使用<比较运算
通过将函数参数这位const引用,我们保证函数可以用于不能拷贝的类型。而且,处理大对象,这种设计策略还能使函数运行得更快。
只使用小于,降低了对处理对象的类型要求,不用支持>
是实际上,如果真的关系类型无关和可移植性,可能需要用less来定义,(不用less,存在的问题是,如果用户调用它比较两个指针,二期两个指针为指向相同的数组,则代码的行为未定义。
template<typename T> int compare(const T &v1, const T &v2)
{
    if(less<T>()(v1,v2)) return -1;
    if(less<T>()(v2,v1)) return 1;
    return 0;
}
模板程序应该尽量减少对实参类型的要求。

当编译器遇到一个模版定义是,它并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。当我们使用(而不是定义)模板是,编译器才生成代码。
通常,当我们调用一个函数时,编译器只需要掌握函数的声明。类似的,当我们使用类对象时,类定义必须是可用的,但是成员函数的定义不必已经出现。因此,我们将类定义和函数声明放在头文件中,而将普通函数和类的成员函数的定义放在源文件中。
但是模板不同:为了生成一个实例化版本,编译器需要掌握函数模板或类模板成原函数的定义。因此,模板的头文件通常即包含声明也包含定义。

当使用模板是,所有不依赖模板参数的名字都必须是可见的,这是有模板的提供者来保证的。而且,模板的提供者必须保证,当模板被实例化时,模板的定义包括类模板的成员定义都是可见的。
用来实例化模板的所有函数、类型以及与类型关联的运算符的声明都必须是可见的,这是由模板的用户来保证的。。

模板知道实例化是才会生成代码,这一特性影响了我们何时才会获知模板内代码的编译错误。通常编译器在三个阶段报告错误。
    第一阶段是编译模板本身时。在这个阶段,编译器通常不会发现很多错误。编译器可以检查语法错误。
    第二阶段是编译器遇到模板使用时。在此阶段,编译器仍然没有很多课检查的。对于函数模板调用,编译器通常会检查实参数目是否正确。它还检查参数类型是否匹配。对于类模板,编译器可以检查用户是否提供了正确数目的模板实参。
    第三个阶段是模板实例化时,只有在这个阶段才能发现类型相关的错误。依赖于编译器如何管理实例化,这类错误可能在链接是才报告。

编译器不能为类模板推断模板参数类型。必须使用尖括号中提供的额外信息。
template <typename T>class Blob(
public:
    typedef T value_type;
    typedef typename std::vector<T>::size_tyep size_type;
    Blob();
    Blob(std::initializer_list<T> il);
    size_type size( ) cosnt {return data->size( );}
    bool empty( ) const { return data->empty( );} 
    void push_back(const T &t) { data->push_back(t);}
    void push_back(T &&t) {data->push_back(std::move(t)); }
    void pop_back();
    T& back();
    T& operator [](size_type i);  
private:
    std::shared_ptr<std::vector<T>> data;
    void check(size_type i, const std::string &msg) const;
} ;
一个类模板的每个实例都是一个相互独立的类。
应该记住类模板的名字不是一个类型名,而是用来实例化类型,而一个实例化的类型总是包含模板参数的。
 一个模板中的代码若果使用了另一个模板,我们通常将模板自己的参数作为被使用模板的实参。
std::shared_ptr<std::vector<T>> data;

类模板的成员函数本身是一个普通函数。但是,类模板的每个实例都有其自己版本的成员函数。因此,类模板的成员函数具有和模板相同的模板参数。因而,定义在类模板之外的成员函数就必须以关键字template开始,后接类模板参数列表。
template<typename T>
void Blob<T>::check(size_type i, const std::string &msg)const
{
    if (i >= data->size( ))
        throw std::out_of_range(mag);
}
template <typename T>
T& Blob<T>::back( )
{
    check(0 , "back on empty Blob");
    return data->back( );
}
template<typename T>
T& Blob<T>:: operator[ ](size_type i)
{
    check(i , "subscript out of range");
    return (*data)[i];
}
template <typename T>
Blob<T>::Blob( ) : data(std::make_shared<std::vector<T>>( ) ) { }
template <typename T>
Blob<T>::Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>> (il)) { }

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

当我们使用一个类模板类型时必须提供模板实参,但这一规则有一个例外。在类模板自己的作用域中,我们可以直接使用模板名而不提供实参,当我们处于一个类模板的作用域中时,编译器处理模板自身引用时就好像我们已经提供了与模板参数匹配的实参一样。
template <typename T>class BlobPtr{
public:
    BlobPtr() : curr(0) { }
    BlobPtr(Blob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
    T& operator*( ) const
    { auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    //注意这里没有模板参数
    BlobPtr& operator++( );
    BlobPtr& operator--();
private:
    std::shared_ptr<std::vector<T>> check (std::size_t ,cosnt std::string&) const;
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr;
};
当我们在类模板外定义其成员是,必须记住,我们并不在类的作用域中,知道遇到类名才表示进入类的作用域:
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator++(int)
{
    BlobPtr ret = *this ;
    ++ *this;
    return ret;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值