More than code

More Than Code
The efficiency of your iteration of reading, practicing and thinking decides your understanding of the world.
  1. 首页
  2. daily
  3. 正文

Daily C/C++ 泛型编程入门

2021年9月1日 596点热度 0人点赞 0条评论

Daily C/C++ 泛型编程入门

参考文章:现代C++实战,Why Not Specialize Function Templates

首先提一个新的概念,鸭子类型

如果一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那么这只鸟就可以被当作鸭子

意思就是一个对象的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定

举个例子就是比如stl中的容器,他们大多数都支持push_back,size等方法,但是他们并没有去统一的继承一个基类(比如sizeableContainer,pushbackableContainer)。

他们只是都统一的实现了这个方法。而鸭子类型就是让开发者可以不使用继承体系来灵活的实现一些约定(size方法等)。而唯一的要求就是这些不同的对象有共同的成员函数,这些成员函数应该有相同的名字和相同结构的参数

C++中的容器类就是这样的,他们没有对象继承关系,但是他们有着很多的同构性。同时这些同构性也比较难用继承来表达,所以这时候我们就需要C++的模板。

模板最基础的作用就是帮助我们抽象类型,让我们将精力放在结构而非类型上

有关模板的使用细节大家可以去参考C++primer,这里也不再多赘述

说一些别的,模板其实就是字面意义上的模板,我们在使用的时候,他会在编译期进行实例化,即按照这个模板生成真的代码。同时要注意,编译器每次看到这个模板的时候都会进行实例化,在最后链接的时候,就会剩下一个。当不同的编译单元看到了不同的定义的话,最终链接的结果是不确定的,所以这里要注意模板实例化要统一。

然后说一下一种很常见的情况,即我们的模板并非完全通用的,他们对某些特定的类型是有限制的,这时候我们要怎么解决呢?

第一种方法,我们可以为那个特定的类型添加对应的方法,从而让这个类型满足我们模板的用法,但是很多情况下这个方法并不可取。因为这会对对应实例化的类型有影响。

第二种方法,如果是函数模板,那么我们可以进行重载

举个例子,假如我们使用的是%这个符号,这个符号对于某些类型是不可用的,这时候我们可以加一个间接层,然后用这个层来抽象这个操作

把原本的a % b改成mod(a, b)

然后对于一般类型,我们这样实现

template<typename T>
T mod(const T &lhs, const T &rhs) {
    return lhs % rhs;
}

而对于特殊类型,我们可以针对他进行重载并实现对应的方法

SpecialType mod(const SpecialType &lhs, const SpecialType &rhs) {
    return special_impl(lhs, rhs);
}

这样我们就有了这个特殊类型的一等公民,调用函数的时候会优先考虑他

第三种方法是使用特化

templace<>
SpecialType mod<SpecialType>(
    const SpecialType &lhs,
    const SpecialType &rhs)
{
    return special_impl(lhs, rhs);
}

可以看到第三种方法和第二种方法十分类似,但是特化是更加通用的方法。因为特化可以用在类模板和函数模板上,而重载只能用在函数上

但是实际使用中,对于函数来说,建议使用重载,而对于类模板,使用特化。

原因大家可以去看一下上面的第二篇参考文章,核心思想就是特化是不可重载的,重载只发生于基础模板之间,所以有的时候基于不同的基础模板我们会得到不同的结果

最后列出参考文章中一个比较有意思的实现

template <bool>
struct compile_time_error;
template <>
struct compile_time_error<true> {};

#define STATIC_ASSERT(Expr, Msg)   \
  {                                \
    compile_time_error<bool(Expr)> \
      ERROR_##_Msg;                \
    (void)ERROR_##_Msg;            \
  }

首先声明一个struct木板,然后特化了true的情况,而对于其他情况没有具体的实现

所以当expr为false的时候,实例化就会报错,同时我们也就得到了对应的错误信息

编译期的多态就是帮助我们解决这种同构的问题,实现代码复用。而动态的多态则是取决于运行时,我们的行为与运行时的动作有关,这两个是两个不同的问题。

标签: c++
最后更新:2021年9月1日

sheep

think again

点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2021 heavensheep.xyz. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS