当前位置:首页 > 科技  > 软件

掌握C++模板的艺术:类型参数、默认值和自动推导

来源: 责编: 时间:2023-12-14 16:37:59 371观看
导读一、模板参数1.类型模板参数在 Grid 示例中,Grid 模板有一个模板参数:存储在网格中的类型。编写类模板时,您需要在尖括号内指定参数列表,例如:template <typename T>这个参数列表类似于函数或方法中的参数列表。与函数和方

kh228资讯网——每日最新资讯28at.com

一、模板参数

1.类型模板参数

在 Grid 示例中,Grid 模板有一个模板参数:存储在网格中的类型。编写类模板时,您需要在尖括号内指定参数列表,例如:kh228资讯网——每日最新资讯28at.com

template <typename T>

这个参数列表类似于函数或方法中的参数列表。与函数和方法一样,你可以编写具有任意多个模板参数的类。此外,这些参数不必是类型,它们可以有默认值。kh228资讯网——每日最新资讯28at.com

2.非类型模板参数

非类型参数是普通参数,如整数和指针——这类参数你可能已经在函数和方法中很熟悉了。然而,非类型模板参数只能是整型(char、int、long 等)、枚举类型、指针、引用、std::nullptr_t、auto、auto& 和 auto*。C++20 还允许浮点类型和类类型的非类型模板参数。后者有很多限制,在本文中不再详细讨论。kh228资讯网——每日最新资讯28at.com

在 Grid 类模板中,你可以使用非类型模板参数来指定网格的高度和宽度,而不是在构造函数中指定。在模板列表中指定非类型参数而不是在构造函数中指定的主要优点是这些值在代码编译之前就已知。回想一下,编译器通过在编译之前替换模板参数来生成模板实例的代码。因此,你可以在实现中使用普通的二维数组,而不是动态调整大小的向量数组。以下是带有更改的新类定义:kh228资讯网——每日最新资讯28at.com

export template <typename T, size_t WIDTH, size_t HEIGHT>class Grid {public:    Grid() = default;    virtual ~Grid() = default;    // 明确默认复制构造函数和赋值运算符。    Grid(const Grid& src) = default;    Grid& operator=(const Grid& rhs) = default;    std::optional<T>& at(size_t x, size_t y);    const std::optional<T>& at(size_t x, size_t y) const;    size_t getHeight() const { return HEIGHT; }    size_t getWidth() const { return WIDTH; }private:    void verifyCoordinate(size_t x, size_t y) const;    std::optional<T> m_cells[WIDTH][HEIGHT];};

注意,模板参数列表需要三个参数:存储在网格中的对象类型,以及网格的宽度和高度。宽度和高度用于创建存储对象的二维数组。下面是类方法的定义:kh228资讯网——每日最新资讯28at.com

// 类方法定义template <typename T, size_t WIDTH, size_t HEIGHT>void Grid<T, WIDTH, HEIGHT>::verifyCoordinate(size_t x, size_t y) const {    if (x >= WIDTH) {        throw std::out_of_range { std::format("{} must be less than {}.", x, WIDTH) };    }    if (y >= HEIGHT) {        throw std::out_of_range { std::format("{} must be less than {}.", y, HEIGHT) };    }}template <typename T, size_t WIDTH, size_t HEIGHT>const std::optional<T>& Grid<T, WIDTH, HEIGHT>::at(size_t x, size_t y) const {    verifyCoordinate(x, y);    return m_cells[x][y];}template <typename T, size_t WIDTH, size_t HEIGHT>std::optional<T>& Grid<T, WIDTH, HEIGHT>::at(size_t x, size_t y) {    return const_cast<std::optional<T>&>(std::as_const(*this).at(x, y));}

注意,之前你在哪里指定了 Grid<T>,现在你必须指定 Grid<T, WIDTH, HEIGHT> 来指定三个模板参数。你可以这样实例化并使用这个模板:kh228资讯网——每日最新资讯28at.com

Grid<int, 10, 10> myGrid;Grid<int, 10, 10> anotherGrid;myGrid.at(2, 3) = 42;anotherGrid = myGrid;cout << anotherGrid.at(2, 3).value_or(0);

这段代码看起来很棒,但不幸的是,存在比你最初预期的更多限制。首先,你不能使用非常量整数来指定高度或宽度。以下代码无法编译:kh228资讯网——每日最新资讯28at.com

size_t height { 10 };Grid<int, 10, height> testGrid; // 无法编译

然而,如果你将高度定义为常量,则可以编译:kh228资讯网——每日最新资讯28at.com

const size_t height { 10 };Grid<int, 10, height> testGrid; // 可编译并工作

具有正确返回类型的 constexpr 函数也可以工作。例如,如果你有一个返回 size_t 的 constexpr 函数,你可以用它来初始化高度模板参数:kh228资讯网——每日最新资讯28at.com

constexpr size_t getHeight() { return 10; }...Grid<double, 2, getHeight()> myDoubleGrid;

第二个限制可能更重要。现在宽度和高度是模板参数,它们是每个网格类型的一部分。这意味着 Grid<int,10,10> 和 Grid<int,10,11> 是两种不同的类型。你不能将一种类型的对象赋值给另一种类型的对象,也不能将一种类型的变量传递给期望另一种类型变量的函数或方法。kh228资讯网——每日最新资讯28at.com

注意:非类型模板参数成为实例化对象类型规范的一部分。kh228资讯网——每日最新资讯28at.com

二、类模板参数的默认值

设置高度和宽度的默认值

如果您继续使用高度和宽度作为模板参数的方法,您可能想为 Grid<T> 类构造函数中之前的高度和宽度非类型模板参数提供默认值。C++ 允许您使用类似的语法为模板参数提供默认值。同时,您也可以为 T 类型参数提供默认值。下面是类定义:kh228资讯网——每日最新资讯28at.com

export template <typename T = int, size_t WIDTH = 10, size_t HEIGHT = 10>class Grid {    // 其余部分与之前版本相同};

在方法定义的模板规范中,您不需要为 T、WIDTH 和 HEIGHT 指定默认值。例如,这是 at() 方法的实现:kh228资讯网——每日最新资讯28at.com

template <typename T, size_t WIDTH, size_t HEIGHT>const std::optional<T>& Grid<T, WIDTH, HEIGHT>::at(size_t x, size_t y) const {    verifyCoordinate(x, y);    return m_cells[x][y];}

现在,您可以在没有任何模板参数的情况下实例化 Grid,只需指定元素类型,元素类型和宽度,或元素类型、宽度和高度:kh228资讯网——每日最新资讯28at.com

Grid<> myIntGrid;Grid<int> myGrid;Grid<int, 5> anotherGrid;Grid<int, 5, 5> aFourthGrid;

请注意,如果您不指定任何类模板参数,您仍然需要指定一组空的尖括号。例如,以下代码无法编译!kh228资讯网——每日最新资讯28at.com

Grid myIntGrid;

类模板参数列表中默认参数的规则与函数或方法相同;也就是说,您可以从右边开始为参数提供默认值。kh228资讯网——每日最新资讯28at.com

三、类模板参数推导(CTAD)

1.自动推导模板参数

类模板参数推导允许编译器自动从传递给类模板构造函数的参数推导出模板参数。例如,标准库中有一个名为 std::pair 的类模板,在 <utility> 中定义,并在第1章中介绍。pair 存储两个可能不同类型的值,通常需要指定为模板参数。例如:kh228资讯网——每日最新资讯28at.com

pair<int, double> pair1 { 1, 2.3 };

为了避免编写模板参数,可以使用一个名为 std::make_pair() 的辅助函数模板。编写自己的函数模板的细节将在本章后面讨论。函数模板一直支持基于传递给函数模板的参数自动推导模板参数。因此,make_pair() 能够根据传递给它的值自动推导出模板类型参数。例如,编译器为以下调用推导出 pair<int, double>:kh228资讯网——每日最新资讯28at.com

auto pair2 { make_pair(1, 2.3) };

使用类模板参数推导(CTAD),不再需要这样的辅助函数模板。编译器现在会根据传递给构造函数的参数自动推导出模板类型参数。对于 pair 类模板,您可以简单地编写以下代码:kh228资讯网——每日最新资讯28at.com

pair pair3 { 1, 2.3 }; // pair3 的类型为 pair<int, double>

当然,这仅在类模板的所有模板参数要么具有默认值,要么用作构造函数中的参数,从而可以推导出来时才有效。请注意,CTAD 要求有一个初始化器才能工作。以下是非法的:kh228资讯网——每日最新资讯28at.com

pair pair4;

许多标准库类支持 CTAD,例如 vector、array 等。kh228资讯网——每日最新资讯28at.com

注意:这种类型推导对 std::unique_ptr 和 shared_ptr 无效。您向它们的构造函数传递 T*,这意味着编译器必须在推导 <T> 或 <T[]> 之间选择,如果选错了就会很危险。因此,请记住,对于 unique_ptr 和 shared_ptr,您需要继续使用 make_unique() 和 make_shared()。kh228资讯网——每日最新资讯28at.com

2.用户定义的推导指南

您也可以编写自己的用户定义推导指南来帮助编译器。这些指南允许您编写模板参数如何被推导的规则。这是一个高级主题,所以不会详细讨论,但会给出一个示例来展示它们的强大功能。假设您有以下 SpreadsheetCell 类模板:kh228资讯网——每日最新资讯28at.com

template <typename T>class SpreadsheetCell {public:    SpreadsheetCell(T t) : m_content { move(t) } { }    const T& getContent() const { return m_content; }private:    T m_content;};

使用自动模板参数推导,您可以创建一个 std::string 类型的 SpreadsheetCell:kh228资讯网——每日最新资讯28at.com

string myString { "Hello World!" };SpreadsheetCell cell { myString };

然而,如果您将 const char 传递给 SpreadsheetCell 构造函数,则类型 T 被推导为 const char,这不是您想要的!您可以创建以下用户定义的推导指南,当向构造函数传递 const char* 作为参数时,使其将 T 推导为 std::string:kh228资讯网——每日最新资讯28at.com

SpreadsheetCell(const char*) -> SpreadsheetCell<std::string>;

这个指南必须在类定义之外但在与 SpreadsheetCell 类相同的命名空间内定义。通用语法如下。explicit 关键字是可选的,其行为与构造函数的 explicit 相同。通常,这样的推导指南也是模板。kh228资讯网——每日最新资讯28at.com

explicit TemplateName(Parameters) -> DeducedTemplate;

本文链接:http://www.28at.com/showinfo-26-45494-0.html掌握C++模板的艺术:类型参数、默认值和自动推导

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 理解Java虚拟机(JVM):优化代码执行效率的内部机制

下一篇: Python 也能干大事,订阅与发布

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • 0糖0卡0脂 旭日森林仙草乌龙茶优惠:15瓶到手29元

    旭日森林无糖仙草乌龙茶510ml*15瓶平时要卖为79.9元,今日下单领取50元优惠券,到手价为29.9元。产品规格:0糖0卡0脂,添加草本仙草汁,清凉爽口,富含茶多酚,保留
  • 一年经验在二线城市面试后端的经验分享

    忠告这篇文章只适合2年内工作经验、甚至没有工作经验的朋友阅读。如果你是2年以上工作经验,请果断划走,对你没啥帮助~主人公这篇文章内容来自 「升职加薪」星球星友 的投稿,坐
  • 让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 本地生活这块肥肉,拼多多也想吃一口

    出品/壹览商业 作者/李彦编辑/木鱼拼多多也看上本地生活这块蛋糕了。近期,拼多多在App首页&ldquo;充值中心&rdquo;入口上线了本机生活界面。壹览商业发现,该界面目前主要
  • 东方甄选单飞:有些鸟注定是关不住的

    作者:彭宽鸿来源:华尔街科技眼&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;东方甄选创始人俞敏洪带队的&ldquo;7天甘肃行&rdquo;直播活动已在近日顺利收官。成立后一
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • OPPO Reno10 Pro英雄联盟定制礼盒公布:萨勒芬妮同款配色梦幻十足

    5月24日,OPPO推出了全新的OPPO Reno 10系列,包含OPPO Reno10、OPPO Reno10 Pro和OPPO Reno10 Pro+三款新机,全系标配了超光影长焦镜头,是迄今为止拍照
Top