C++关键概念 泛型编程 模板的元编程 印第安纳 得克萨斯

C++关键概念 泛型编程

泛型编程是C++中的一项关键概念,它允许程序员在定义数据结构、算法或类时使用类型参数。这些类型参数可以是任何数据类型,如整数、浮点数、字符串等。通过使用泛型,程序员可以编写出更加灵活、可重用的代码,而不需要为每种数据类型都编写特定的代码。

泛型编程的实现主要依赖于模板这一关键技术。模板是一种将数据类型作为一个参数的特殊函数或类,它可以在编译时根据实际的数据类型生成相应的代码。通过使用模板,程序员可以为不同类型的数据提供统一的处理逻辑。

在C++中,泛型编程主要通过模板来实现。程序员可以自定义模板类和模板函数,以满足特定的需求。这个过程称为模板实例化。当编译器遇到模板代码时,它会使用推断出的模板参数来创建一个特定版本的函数或类实例。整个实例化过程发生在编译阶段。

此外,泛型编程支持隐式接口,这是基于有效的表达式推断出来的。这与面向对象编程支持的显式接口有所不同,显式接口由函数名称、参数类型、返回类型构成。

值得注意的是,泛型类或泛型接口中的函数,控制函数重载的规则是相同的。同时,类型参数要遵循的限制与类和接口中的类型参数相同。

总的来说,泛型编程为C++程序员提供了一种强大的工具,使他们能够编写出更加灵活、可重用和高效的代码。通过使用模板和类型参数,程序员可以创建出能够处理多种数据类型的算法和数据结构,从而提高了代码的可维护性和可扩展性。

对 C++ 来说,泛型编程和使用模板的元编程已经取得了巨大的成功。但是,对泛型组件的接口却迟迟未能以一种令人满意的方式进行合适的规范

例如,在 C++98 中,标准库算法大致是如下规定的:

template<typename Forward_iterator, typename Value>
ForwardIterator find(Forward_iterator first, Forward_iterator last,
                              const Value & val)
{
   
    while (first != last && *first != val)
        ++first;
    return first;
}

C++ 标准规定:

  • 第一个模板参数必须是前向迭代器。
  • 第二个模板参数类型必须能够使用 == 与该迭代器的值类型进行比较。
  • 前两个函数参数必须标示出一个序列。

这些要求是隐含在代码中的:编译器所要做的就是在函数体中使用模板参数。结果是:极大的灵活性,对正确调用生成出色的代码,以及对不正确的调用有糟糕得一塌糊涂的错误信息。解决方案显而易见,将前两项条件作为模板接口的一部分来指定:

template<forward_iterator Iter, typename Value>
    requires equality_comparable<Value, Iter::value_type>
forward_iterator find(Iter first, Iter last, const Value& val);

这大致就是 C++20 所提供的了。注意 equity_comparable 概念,它捕获了两个模板参数之间必需有的关系。这样的多参数概念非常常见。

表达第三个要求([first:last) 是一个序列)需要一个库扩展。C++20 在 Ranges 标准库组件(§9.3.5)中提供了该特性:

template<range R, typename Value>
    requires equality_comparable<Value, Range::value_type>
forward_iterator find(R r, const Value& val)
{
   
    auto first = begin(r);
    auto last = end(r);
    while (first!=last && *first!=val)
        ++first;
    return first;
}

为了规范模板对其参数的要求,对其提供良好支持,有过数次尝试。本节会进行描述:

  • §6.1:概念的早期历史
  • §6.2:C++0x 中的概念
  • §6.3:Concepts TS
  • §6.4:C++20 中的概念

6.1 概念的早期历史

1980 年,我猜想泛型编程可以通过 C 风格的宏来有效支持 [Stroustrup 1982]。然而我完全错了。一些有用的简单泛型抽象能通过这种方法表达,1980 年代的标准化之前的 C++ 通过 <generic.h> 中的一组宏为泛型编程提供支持,但宏在大型项目或广泛使用的情况下无法有效管理。尽管泛型编程在当时流行的“面向对象的思想”中并没有一席之地,我确实发现了一个问题,需要解决它才能达到我对“带类的 C”的目标。

大约在 1987 年,我尝试设计具有合适接口的模板 [Stroustrup 1994],但失败了。我需要三个基本属性来支持泛型编程:

  • 全面的通用性/表现力——我明确不希望这些功能只能表达我想到的东西。
  • 与手工编码相比,零额外开销——例如,我想构建一个能够与 C 语言的数组在时间和空间性能方面相当的 vector。
  • 规范化的接口——我希望类型检查和重载的功能与已有的非泛型的代码相类似。

那时候没人知道如何做到全部三个方面,因此 C++ 所做到的是:

  • 图灵完备性 [Veldhuizen 2003]
  • 优于手动编码的性能
  • 糟糕的接口(基本上是编译期鸭子类型),但仍然做到了静态类型安全

前两个属性使模板大获成功。

由于缺乏规范化的接口,我们在这些年里看到了极其糟糕的错误信息,到了 C++17 还仍然是这样。缺乏规范化的接口这一问题,让我和很多其他人困扰很多年。它让我非常困扰的原因是,模板无法满足 C++ 的根本的设计标准 [Stroustrup 1994]。我们(显然)需要一种简单的、没有运行期开销的方法来指定模板对其模板参数的要求。

多年以来,一些人(包括我)相信模板参数的要求可以在 C++ 本身中充分指定。1994 年,我在 [Stroustrup 1994] 中记录了基本的想法,并在我的网站上发布了示例 [Stroustrup 2004–2020]。自 2006 年以来,基于 Jeremy Siek 的作品,Boost 提供了该想法的一个变体,Boost 概念检查库 [Siek and Lumsdaine 2000–2007]。不知何故,它并未像我所希望的那样广泛流行。我怀疑原因是它不够通用、不够优雅(Boost 感到有义务使用宏隐藏细节),并且在标准中不受支持。许多人将其视为一种奇技淫巧。

为 C++ 定义的概念可以追溯到 Alex Stepanov 在泛型编程上的工作,这是 1970 年代末开始的,一开始用的名称是“代数结构” [Kapur et al. 1981]。注意,那差不多比 Haskell 的类型类设计 [Wadler and Blott 1989] 要早十年,比我尝试解决 C++ 的类似问题要早 5 年。对于这种需求,Alex Stepanov 早在 1990 年代末期的讲座中就使用了“概念”这一名称,并记录在 [Dehnert and Stepanov 2000]。我之所以提到这些,是因为许多人猜测概念是从 Haskell 类型类派生而来但被错误命名了。Alex 使用“概念”这一名称是因为概念此处用来代表应用领域(如代数)中的基本概念。

目前把概念当作依靠使用模式来描述操作的类型谓词,这起源于二十一世纪初期 Bjarne Stroustrup 和 Gabriel Dos Reis 的工作,并记录在 [Dos Reis and Stroustrup 2005b, 2006; Stroustrup and Dos Reis 2003b, 2005a] 之中。这种方法在 1994 年的《设计和演化》[Stroustrup 1994] 一书也被提及,但是我不记得我第一次进行尝试的时间了。将概念建立于使用模式的主要原因是为了以一种简单而通用的方式处理隐式转换和重载。我们了解 Haskell 类型类,但它们对当前的 C++ 设计影响不大,因为我们认为它们太不灵活了。

精确指定并检查一个模板对于参数的要求曾经是 C++0x 的最出彩之处,会对泛型编程提供关键支持。可是,它最终甚至没能进入 C++17。

Bjarne Stroustrup 和 Gabriel Dos Reis 在 2003 年发表的论文 [Stroustrup 2003; Stroustrup and Dos Reis 2003a,b] 明确指出,概念是简化泛型编程的宏伟计划的一部分。例如,一个 concept 可以被定义为一组使用模式的约束,就是说,作为对某种类型有效的语言构件 [Stroustrup and Dos Reis 2003b]:

concept Value_type {
   
    constraints(Value_type a)
    {
   
        Value_type b = a;      // 拷贝初始化
        a = b;                 // 拷贝赋值
        Value_type v[] = {
   a};  // 不是引用
    }
};

template<Value_type V>
    void swap(V& a, V& b);  // swap() 的参数必须是值类型

但是,当时的语法和语义还很不成熟。我们主要是试图建立设计标准 [Stroustrup and Dos Reis 2003a]。从现代(2018 年)的角度来看,[Stroustrup 2003; Stroustrup and Dos Reis 2003a,b] 有很多缺陷。但是,它们为概念提供了设计约束,并在以下方面提出了建议:

  • 概念——用于指定对模板参数要求的编译期谓词。
  • 根据使用模式来指定原始约束——以处理重载和隐式类型转换。
  • 多参数概念——例如 Mergeable<In1,In2,Out>
  • 类型和值概念——也就是说,概念既可以将值也可以将类型当作参数,例如 Buffer<unsigned char,128>
  • 模板的“类型的类型”简略写法—例如 template<Iterator Iter> …
  • “模板定义的简化写法”——例如 void f(Comparable&); 使泛型编程更接近于“普通编程”。
  • auto 作为函数参数和返回值中约束最少的类型。
  • 统一函数调用(§8.8.3)——减少泛型编程与面向对象编程之间的风格差异问题(例如 x.f(y)f(x,y)x+y)。

奇怪的是,我们没有建议通用的 requires 子句(§6.2.2)。这些都是后面所有概念变体的一部分。

6.2 C++0x 概念

2006 年,基本上每个人都期望 [Gregor et al. 2006; Stroustrup 2007] 中所描述的概念版本会成为 C++09 的一部分,毕竟它已经投票进入了 C++ 标准草案(工作文件)。但是,C++0x 变成了 C++11,并且在 2009 年,概念因复杂性和可用性问题陷入困境 [Stroustrup 2009a,b],委员会以绝对多数票一致同意放弃概念设计 [Becker 2009]。失败的原因多种多样,而且可能使我们获得在 C++ 标准化努力之外的教训。

在 2004 年,有两项独立的工作试图将概念引入 C++。因为主要支持者分别来自印第安纳大学和得克萨斯农工大学,这两派通常就被称为“印第安纳”和“得克萨斯”:

  • 印第安纳:一种与 Haskell 类型类相关的方法,主要依赖于操作表来定义概念。这派认为,程序员应当显式声明一个类型“模拟”了一个概念;也就是说,该类型提供了一组由概念指定的操作 [Gregor et al. 2006]。关键人物是 Andrew Lumsdaine(教授)和 Douglas Gregor(博士后和编译器作者)。
  • 得克萨斯:一种基于编译期类型谓词和谓词逻辑的方法。这派认为,可用性很重要,因而程序员不必显式指定哪些类型与哪些概念相匹配(这些匹配可以由编译器计算)。对于 C++,优雅而有效地处理隐式转换、重载以及混合类型的表达式被认为是必需的 [Dos Reis and Stroustrup 2006; Stroustrup and Dos Reis 2003b]。关键人物是 Bjarne Stroustrup(教授)和 Gabriel Dos Reis(博士后,后来成为教授)。

根据这些描述,这些方法似乎是不可调和的,但是对于当时的参与人员而言,这并不明显。实际上,我认为这些方法在理论上是等效的 [Stroustrup and Dos Reis 2003b]。该论点的确可能是正确的,但对于 C++ 上下文中的详细语言设计和使用的实际影响并不等同。另外,按照委员会成员的解释,WG21 的共识流程强烈鼓励合作和联合提案,而不是在竞争性的提案上工作数年,最后在它们之间进行大决战(§3.2)。我认为后一种方法是创造方言的秘诀,因为失败的一方不太可能放弃他们的实现和用户,并就此消失。请注意,上面提到的所有的人在一起与 Jeremy Siek(印第安纳的研究生和 AT&T 实验室的暑期实习生)和 Jaakko Järvi(印第安那的博士后,得州农工大学教授)是 OOPSLA 论文的合著者,论文展示了折中设计的第一个版本。印第安纳和得克萨斯的团体从未完全脱节,我们为达成真正的共识而努力。另外,从事这项工作之前,我已经认识 Andrew Lumsdaine 很多年。我们确实希望折中方案能够正常工作。

在实现方面,印第安纳的设计的进度远远领先于得克萨斯的设计的进度,并且具有更多人员参与,所以我们主要基于此进行。印第安纳的设计也更加符合常规,基于函数签名,并且与 Haskell 类型类有明显相似之处。考虑到涉及的学术界人士的数量,重要的是印第安纳的设计被视为更符合常规并且学术上更为得体。看来我们“只是”需要

  • 使编译器足够快
  • 生成有效的代码
  • 处理重载和隐式转换。

这个决定使我们付出了三年的辛勤工作和许多争论。

C++0x 概念设计在 [Gregor et al. 2006; Stroustrup 2007] 中得到阐述。前一篇论文包含一个标准的学术“相关工作”部分,将这个设计与 Java、C#、Scala、Cecil、ML、Haskell 和 G 中的工具进行比较。在这里,我使用 [Gregor et al. 2006] 中的例子进行总结。

6.2.1 概念定义

概念被定义为一组操作和相关类型:

concept EqualityComparable<typename T> {
   
    bool operator==(const T& x, const T& y);
    bool operator!=(const T& x, const T& y) {
    return !(x==y); }
}

concept InputIterator<typename Iter> {
   
    // Iter 必须有 value_type 成员:
    typename value_type = Iter::value_type;
    // ...
}

某些人(印第安纳)认为概念和类之间的相似性是一种优势。

但是,概念中指定的函数并不完全类似于类中定义的函数。例如,在一个 class 中定义的运算符具有隐式参数(“this”),而 concept 中声明的运算符则没有。

将概念定义为一组操作的方法中存在一个严重的问题。考虑在 C++ 中传递参数的方式:

void f(X);
void f(X&);
void f(const X&);
void f(X&&);

暂时不考虑 volatile,因为它在泛型代码参数中很少见到,但是我们仍然有四种选择。在一个 concept 中,我们是否

  • f 表示为一个函数,用户是否为调用选择了正确的参数?
  • 是否重载了 f 的所有可能?
  • f 表示为一个函数,并要求用户定义一个 concept_map(§6.2.3)映射到 f 的所需的参数类型?
  • 语言是否将用户的参数类型隐式映射到模板的参数类型?

对于两个参数,我们将有 16 种选择。尽管很少有三个参数泛型函数,但是这种情况我们会有 4*4*4 种选择。变参模板会如何呢?我们会有 4N 种选择,如(§4.3.2)。

传递参数的不同方式的语义并不相同,因此我们自然而然地转向接受指定的参数类型,将匹配的负担推到了类型设计者和 concept_maps 的作者(§6.2.3)。

类似地,我们到底是在为

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/576445.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RAG原理及本地化实践

基于LLM的应用在问题回答、信息获取上发挥出了巨大作用。这些通用大模型训练的数据主要来源于互联网上的会话或者个别机构提供的数据&#xff0c;虽然能够提供类似人的交互对答&#xff0c;但是在针对某个特定领域的时候就显得不足。通用大模型在应用中主要有以下问题&#xff…

【DINO】环境配置

1. DINO简介 作为一款基于Transformer性能强劲的计算机视觉算法&#xff0c;一经发布即受追捧&#xff0c;本文记录下在DINO官方代码在集群上的环境配置及训练自己的数据集过程。 DINO原文&#xff1a;https://arxiv.org/abs/2203.03605 DINO源代码&#xff1a;https://github.…

ssm084基于ssm的大型商场会员管理系统+jsp

大型商场会员管理系统的设计与实现 摘 要 进入信息时代以来&#xff0c;很多数据都需要配套软件协助处理&#xff0c;这样可以解决传统方式带来的管理困扰。比如耗时长&#xff0c;成本高&#xff0c;维护数据困难&#xff0c;数据易丢失等缺点。本次使用数据库工具MySQL和编…

【C语言必刷题】7. 百钱百鸡

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

《汇编语言》- 读书笔记 - 综合研究

《汇编语言》- 读书笔记 - 综合研究 研究试验 1 搭建一个精简的 C 语言开发环境1. 下载2. 配置3. 编译4. 连接 研究试验 2 使用寄存器1. 编一个程序 ur1.c &#xff08; tcc 用法&#xff09;tcc 编译连接多个源文件tlink 手动连接 2.用 Debug 加载 ur1.exe&#xff0c;用u命令…

数据转换 | Matlab基于RP递归图一维数据转二维图像方法

目录 效果分析基本介绍程序设计参考资料获取方式 效果分析 基本介绍 Matlab基于RP递归图一维数据转二维图像方法 基于RP&#xff08;Recurrence Plot&#xff09;递归图的方法可以将一维数据转换为二维图像&#xff0c;以可视化数据的动态特征。RP递归图是一种表示时间序列相…

android 去除桌面谷歌搜索框

注&#xff1a; 本文只是博主学习记录分享&#xff0c;仅供参考。如有错误请指出来&#xff0c;谢谢&#xff01; 一、问题描述 去除 android 系统桌面谷歌搜索栏&#xff0c;前后对比如下图&#xff1a; 系统版本&#xff1a;android12 平台&#xff1a;rk3568 二、…

【小浩算法cpp题解】判断环形链表

目录 前言我的思路思路一 &#xff08;哈希表记录链表的访问&#xff09;&#xff1a;思路二 &#xff08;双指针&#xff0c;快指针在前&#xff0c;慢指针在后&#xff09;&#xff1a; 我的代码运行结果 前言 前几天我写的代码&#xff0c;都是把所有的内容写在main函数里&…

Veeam配置备份oracle实例

Veeam是一家专门提供数据管理和数据保护解决方案的软件公司。他们的产品主要包括备份、复制和虚拟化管理等功能&#xff0c;旨在帮助企业保护其数据、应用程序和系统&#xff1b;NBU&#xff0c;COMMVALT&#xff0c;Veeam 国际三大知名备份软件厂商。本文介绍使用Veaam 备份Li…

【nodejs状态库mobx之computed规则】

The above example nicely demonstrates the benefits of a computed value, it acts as a caching point. Even though we change the amount, and this will trigger the total to recompute, it won’t trigger the autorun, as total will detect its output hasn’t been …

行人属性AI识别/人体结构化属性AI识别算法的原理及应用场景介绍

行人属性AI识别技术是一种基于人工智能技术的图像识别技术&#xff0c;通过对行人的图像或视频进行处理和分析&#xff0c;提取出其中的结构化信息&#xff0c;如人体姿态、关键点位置、行人属性&#xff08;性别、年龄、服装等&#xff09;等。 行人结构化数据分析的方法包括…

LORA详解

第一章、lora论文解析 参考论文&#xff1a; low rank adaption of llm 背景介绍&#xff1a; 自然语言处理的一个重要范式包括对一般领域数据的大规模预训练和对特定任务或领域的适应处理。在自然语言处理中的许多应用依赖于将一个大规模的预训练语言模型适配到多个下游应用…

小程序变更主体还要重新备案吗?

小程序迁移变更主体有什么作用&#xff1f;小程序迁移变更主体的作用可不止变更主体这一个哦&#xff01;还可以解决一些历史遗留问题&#xff0c;比如小程序申请时主体不准确&#xff0c;或者主体发生合并、分立或业务调整等情况。这样一来&#xff0c;账号在认证或年审时就不…

五一~感恩回馈,SolidKits工具折扣来袭!

SOLIDWORKS插件多样且丰富&#xff0c;有着不同的种类和用途&#xff0c;可以为SOLIDWORKS软件本身提升使用效率&#xff0c;更快速的响应你的操作方式。SolidKits自主设计研发多款SOLIDWORKS增效插件&#xff0c;包括&#xff1a;自动化参数设计插件、高级BOM插件、批量编码器…

【leetcode面试经典150题】75. 二叉树展开为链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

Weblogic JMS

简介 全称:WebLogic Server的Java Messaging Service(JMS) WebLogic JMS 是与 WebLogic Server 平台紧密集成的企业级消息传递系统。 Java Message Service (JMS) API 是一种消息传递标准,允许基于 Java Platform Enterprise Edition (Java EE) 的应用程序组件创建、发送、…

基于STC12C5A60S2系列1T 8051单片机正常模式或移位模式控制数码管某位闪烁后单击长按增加或减少数值应用

基于STC12C5A60S2系列1T 8051单片机正常模式或移位模式控制数码管某位闪烁后单击长按增加或减少数值应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍基于STC12C5A6…

MySQL Workbench 数据库常用操作

大家好哦&#xff0c;我是程序员徐师兄&#xff0c;今天为大家打来的是MySQL Workbench 数据库常用操作。 文章目录 一、连接数据库二、进入数据库三、创建数据库四、设置默认数据库五、创建数据表六、查看表数据七、查看数据表 一、连接数据库 二、进入数据库 三、创建数据库 …

【Leetcode】vector刷题

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;Leetcode刷题 目录 1.只出现一次的数字2.杨辉三角3.删除有序数组中的重复项4.只出现一次的数字II5.只出现一次的数字III6.电话号码的字母组合 1.只出现一次的数字 题目链接&#xff1a;136.只出现一…

vivado 创建和运行链路清扫

创建和运行链路清扫 要分析给定链路的裕度 &#xff0c; 利用不同 MGT 设置来多次运行链路扫描是很有效的。这样有助于判定最佳设置。 Vivado Serial I/O Analyzer 功能支持您定义、运行、保存和重新调用链路清扫 &#xff0c; 链路清扫是由多次链路扫描集合而成的。 每条…
最新文章