C++泛型编程泛泛谈

2022-11-29 14:49:06 浏览数 (1)

云飞兄是你先拔的剑,就别怪我也亮剑了 李云龙

害,最近刷短视频都是亮剑,好好看,下面也放一首主题曲,感受一下“中国军魂”~

越来越读不下去传统的一些编程书了,我个人反思了一下,我觉得不是书的毛病,是我的毛病,这些书的出发点都是初学者或是稍微有点基础的读者,经常是对一个概念解释很多次,翻来覆去的说,而且给的demo看起来也比较呆瓜。其实实用性还是差很多的,看代码里面都是使用的一些新标准新特性,但是这些书都是有点老了,也不讲。

这里可能要插一个东西叫,元编程:

模板元编程把模板的一些技术(特化、实例化、 SFINAE )当成模板元编程这种特定语言的控制流。例如 fact<10>::value 会在计算 value 的过程中实例化 11 个 fact ( i 从 0 到 10 )的空类。实例化代码是计算手段,是递归的中间结果。

而泛型编程则是将模板用特定的类型来实例化,例如将模板类 list实例化成真正的类 list。实例化代码是最终目的。

先学泛型编程再学元编程先学泛型编程再学元编程!

只有这本是讲这个的

我需要的只是一个精简的高质量demo和言简意赅的概念解释而已,幸好我今天找到了这样的文档,那就是微软的C :

已经很久没有可以连续看一个技术文档这么久了,这种点到为止的感觉真的很好。

里面还有一个日语写的程序段,感觉有点莫名其妙的可可爱爱

而且C 研究的越多越觉得,范型编程或者说通用编程才是C 这个语言的灵魂,在2022年还拿C 和C比的人,我只能说你格局太low了,朋友~

别杠,杠就叉出去

面向对象编程(OOP)和泛型编程都可以处理编写程序时不知道类型的情况;二者的不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译时就能获知类型了。

我们所常用的STL标准库中,每一个容器都提供了单一的,泛型的定义,例如我们所常用的vector,我们可以定义很多类型的vector:

代码语言:javascript复制
vector<int> vi; // vi是装载int类型的vector容器的实例
vector<string> vs; // vs是装载string类型的vector容器的实例
vector<double> vd; // vd是装载double类型的vector容器的实例

我这里是想说明一种读源码的顺序,<>里面是具体的类型,最前面是容器,最后面的是建议的一个实例。

模板是 C 中的泛型编程的基础。作为强类型语言,C 要求所有变量都具有特定类型,由程序员显式声明或编译器推导。但是,许多数据结构和算法无论在哪种类型上操作,看起来都是相同的。使用模板可以定义类或函数的操作,并让用户指定这些操作应处理的具体类型。

模板的书这个感觉还行

你看这个模版的定义,就说的很明白,模版就是定义了一种操作,淡化了具体类型的工作,其实是将这个工作放到了别的流程上面。

模板是基于用户为模板参数提供的参数在编译时生成普通类型或函数的构造。例如,可以定义如下所示的函数模板:

代码语言:javascript复制
template <typename T>
T minimum(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

函数定义以关键字template开始,后跟一个模板参数列表,这是一个逗号分隔的一个或多个模板参数的列表,用尖括号包围起来。

**注:**在模板定义中,模板参数列表不能为空

模板参数列表表示在类或函数定义中用到的类型或者值。当我们使用模板的时候,可以(显式或隐式地)指定模板实参,将其绑定到模板参数上。

上面的代码描述了一个具有单个类型参数 T 的泛型函数的模板,其返回值和调用参数(lhs 和 rhs)都具有此类型。

可以随意命名类型参数,但按照约定,最常使用单个大写字母。T 是模板参数;关键字 typename 表示此参数是类型的占位符。调用函数时,编译器会将每个 T 实例替换为由用户指定或编译器推导的具体类型参数。编译器从模板生成类或函数的过程称为“模板实例化”;minimum 是模板 minimum 的实例化。

当编译器遇到一个模板定义的时候,它并不会生成代码。只有我们实例化出模板的一个特定的版本时,编译器才会生成其对应的代码。当我们使用(而不是定义)模板时,编译器才会生成代码。这个特性影响我们如何组织代码以及错误何时才可以被检测到。

通常来说,我们将类定义和函数说明放在头文件中,而普通函数和类的成员函数的定义放在源文件中,模板则不尽相同:为了生成一个实例化的版本,编译器需要掌握函数模板或类模板成员函数的定义。

总结:与非模板代码不同,模板的头文件通常既包括声明也包括定义,即函数模板和类模板成员函数的定义通常放在头文件中。

编译出现错误的时机:

第一阶段,编译模板本身时,该时期所出现的错误大多数为语法错误;

第二阶段,编译器遇到模板使用时;

第三阶段,模板实例化时,而只有在这个阶段才能发现类型相关的问题。

上面说的都是函数模板,还有一种是类模板。类模板是用来生成类的蓝图的。与函数模板不同之处是,编译器不能为类模板推断模板参数类型。所以我们必须在模板名后的尖括号中提供额外的信息——用来替代模板参数的模板实参列表。

代码语言:javascript复制
template<typename T>
class T_vector {
public:
  typedef T value_type;
    // 构造函数
    T_vector() =default;
    T_vectot(std::initializer_list<T> il);
    // 容器的元素数目
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加元素
    void push_back(const T& val) { 
        data->push_back(val);
    }
    void push_back(T &&val) {
        data->push_back(std::move(val));
    }
private:
    std::shared_ptr<std::vector<T> > data;
    // 若data[i]无效,则抛出msg
    void check(size_type i,const std::string &msg) const;
}

类似函数模板,类模板以关键字template开始,后跟模板参数列表。在类模板(及其成员)的定义中,我们将模板参数当作替身,代替使用模板时用户需要提供的类型或值。

**注:**一个类模板的每一个实例都形成一个独立的类,而类模板的每个实例都有其自己版本的成员函数

所以,我们可能会出现一个单一模板并不能满足所有类型的需求,而模板特例化就出现了。

类模板成员函数的实例化

默认的情况下,一个类模板的成员函数只有在程序用到它的时候才会实例化。

函数重载与模板特例化的区别

当定义函数模板的特例化版本时,我们本质上接管了编译器的工作。即,我们为原先的模板的其中一个特殊的实例提供了定义。简而言之,特例化的本质是实例化一个模板,而非重载它,因此特例化并不影响函数匹配。

注意事项:

为了特例化一个模板,原模版的声明必须在作用域中。在任何使用模板实例的代码之前,特例化版本的声明也必须在作用域中。所有同名模板的声明应该放在前面,然后是这些模板的特例化版本。

类模板部分特例化

与函数模板不同的是,类模板的特例化不必为所有模板参数提供实参。一个类模板的部分特例化本身是一个模板,使用它时用户还必须为那些在特例化版本中指定的模板参数提供实参。

注:我们只能部分特例化类模板,而不能部分特例化函数模板。

被窝写文章就是不一样,写完直接合住电脑说晚安,晚安!

代码语言:javascript复制
作者:匿名用户链接:https://www.zhihu.com/question/31064902/answer/951581334来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码语言:javascript复制
————————————————
版权声明:本文为CSDN博主「wzh_scuec」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_51958878/article/details/123852228

0 人点赞