C++ 模板:以简御繁-5/5

免费升级哨兵2号分辨率!用SRCNN深度学习模型实现10米→2.5米超分重建 本文详细介绍了如何利用SRCNN深度学习模型,将哨兵2号遥感影像的分辨率从10米免费提升至2.5米。文章提供了从数据准备、模型训练到部署应用的全流程实战指南,旨在通过AI应用技术,为农业监测、城市规划等领域提供更高精度的影像数据支持。 阅读详情

 英文原文:https://victor-istomin.github.io/c-with-crosses/posts/templates-are-easy/

C++ 模板:以简御繁-1/5

C++ 模板:以简御繁-2/5

C++ 模板:以简御繁-3/5

C++ 模板:以简御繁-4/5

C++ 模板:以简御繁-5/5

10. 可变参数模板

还有一个主题,如果开发者谨慎使用,可以从中受益匪浅。过去,我们有一些函数,它们的参数数量是可变的。然而,现在有一组类似 printf 的函数,开发者可以传入可变数量的参数,编译器会尽力避免他们犯下许多错误。

例如,虚幻引擎将格式字符串检查合并到自定义预处理器中,而 std::format 或 fmt 为我们提供了一个有点神秘但比运行时的未定义行为要好得多的错误消息。

既然如此,为什么不为 makeString("xs: ", xs, "; and the double is: ", PI) 提供一个可变参数模板呢?至少,这是一个不错的练习。

10.1 基本语法和注意事项

template <typename... Args>
constexpr int uselessCount(Args&&... args)
{
    return sizeof...(args);
}

int main(int argc, char**)
{
    return uselessCount(1,2,3) 
          + uselessCount();
}

让我逐行描述这部杰作

第 1 行使用显而易见的语法声明了一个参数包。参数包是一个模板参数,它接受零个或多个模板参数。

第 2 行:“Args&&… args”是对包的转发引用。第 4 行:一个特殊的 sizeof... 函数,它在编译时被评估为包的参数数量。

第 9 行:一种显而易见的使用方法。

第 10 行可能不太显而易见,但仍然正确:提供了一个零个参数的包。

10.1.1 参数包扩展和递归函数

函数参数除了包含参数包之外,还可以包含一个常规参数。对于递归函数来说,它提供了一种便捷的方法,可以在参数包为空时中断递归。考虑以下伪代码中的 makeString(T&& first, Rest&&... rest) :

Rest 包含参数:return makeString(first) + makeString(rest...); Rest 是一个空参数包:return makeString(first);

虽然上面的伪代码演示了这个想法,但它并不适用于我们的代码:makeString(T&& first, Rest&&... rest) 接受一个或多个参数。由于我们已经有一组单参数实现,因此空包调用会产生歧义。解决方案是限制可变参数重载至少接受两个参数。

这是以前的一种做法:递归函数 makeString(First&& first, Second&& second, Rest&&... rest) 接受至少两个参数以及一个可选的可变参数包。它将第一个参数转换为字符串,然后递归调用 makeString(second, rest...)。一旦 rest... 包为空,它将扩展为非递归的 makeString(second) 调用。

// makeString.hpp
// ... single-parameter implementations cut from the listing  ...

template <typename First, typename Second, typename... Rest>
std::string makeString(First&& first, Second&& second, Rest&&... rest)
{
    return makeString(std::forward<First>(first)) 
         + makeString(std::forward<Second>(second), std::forward<Rest>(rest)...); 
}

// main.cpp
// ... stripped ...
std::cout << makeString("a ", 
                        std::string_view("variadic " ), 
                        std::string("with a double: "), 
                        3.14)
          << std::endl;

第 8 行向读者介绍了参数包扩展:编译器将表达式 (pack)... 扩展为零个或多个表达式 (参数) 模式的逗号分隔列表。例如,std::forward<Rest>(rest)... 扩展为:

如果 Rest 没有参数:扩展为空,忽略第 8 行的 std::forward<Rest>(rest)... 表达式。

如果 Rest 有一个参数:则添加 std::forward<Rest_0>(rest_0),从而产生类似 makeString(std::forward<Second>(second), std::forward<Rest_0>(rest_0)); 的调用。

如果 Rest 有 N 个参数,则每个参数都使用封闭表达式单独扩展:std::forward<Rest_0>(rest_0), std::forward<Rest_1>(rest_1), ..., std::forward<Rest_N>(rest_N)。综上所述,第 8 行 makeString 调用的参数数量取决于参数包的大小。对于每个添加的参数,整个 std::forward<Rest_#>(rest_#) 表达式都会被“复制粘贴”。需要注意的是,这种行为适用于任何类型的打包表达式,而不仅仅是 std::forward,因此 f(g(pack)...) 将被相应地扩展为 f(g(pack_0), g(pack_1), ..., g(pack_N))。

鉴于此,编译、运行并享受!

a: A; b: B{1}; pi: 3.141593
xs: 1;2;3; ys: 4.000000;5.000000;6.000000; zs: 7.000000;8.000000;9.000000
Hello, world!!1
two ; non-copyables
C{"a container with its own to_string()"}
a variadic with a double: 3.140000

10.1.2 受限版本

但是,我们可以进一步改进。与其使用 Second&& 第二个参数来避免重载冲突,不如通过要求 sizeof...(Rest) > 0 来利用约束:

template <typename First, typename... Rest>
    requires (sizeof...(Rest) > 0)
std::string makeString(First&& first, Rest&&... rest)
{
    return makeString(std::forward<First>(first)) 
         + makeString(std::forward<Rest>(rest)...); 
}

10.1.3 折叠表达式

嗯,从 C 17 开始,我们就有了折叠表达式。有了参数包 pack 和一个一元或二元运算“x”,我们就可以把 (pack x ...) 展开为 (pack_0 x (pack_1 x pack_2)),依此类推。折叠表达式有多种选择,包括左结合折叠 (... pack x)(左边带点)、右结合折叠(右边带点)、一元或二元运算选项、空包的初始化变量等等。

为了使本文篇幅合理,我大胆地将读者转发到 Fluent C++ 博客以获取深入的详细信息:第 1 部分 - 基础知识和第 2 部分 - 高级用法。

为了使本文篇幅合理,我大胆地将读者转发到 Fluent C++ 博客以获取深入的详细信息:第 1 部分 - 基础知识和第 2 部分 - 高级用法。

当我们处理字符串时,我期望左结合折叠运算符 = 比使用运算符具有更好的性能,因为后者会在涉及多个字符串的情况下生成临时变量来保存中间结果:return ((a b) c) d 除了 a、b、c 之外还会产生 3 个临时变量,而 return ((a = b) = c) = d; 会避免它们。

template <typename... Pack>
    requires (sizeof...(Pack) > 1)
std::string makeString(Pack&&... pack)
{
    return (... += makeString(std::forward<Pack>(pack)));
}

最后,我想总结一下:

模板函数可以(或许应该)受到限制,以便编译器尽早失败,并在误用的情况下提供简洁的错误上下文。即使它还没有重载。

当概念不可用时,SFINAE 是一个合理的后备方案。

使用 static_assert 和故意的编译失败来深入了解模板扩展,以防误解。

完美转发是我们的朋友:它可能提供更好的性能,并放宽对参数的“可复制”要求。

……但不适用于琐碎类型。有时,通过复制传递比通过引用传递更便宜。

可变参数模板是连续处理多个参数的便捷方法。折叠表达式可能更好。

11. 最终的代码

// makeString.hpp
#pragma once
#include <string>
#include <type_traits>
#include <concepts>

template <typename T>
concept IsContainer = requires (T&& container) { std::begin(container); };

// IsString is more constrained than IsContainer, so it will have a priority wherever possible
template <typename T>
concept IsString = IsContainer<T> && std::constructible_from<std::string, T>;

template <typename T>
concept HasStdConversion = requires (T number) { std::to_string(number); };

template <typename T>
concept HasToString = requires (T&& object) 
{ 
    {object.to_string()} -> std::convertible_to<std::string>; 
};


template <HasStdConversion Numeric>
std::string makeString(Numeric number)
{
    return std::to_string(number);
}

template <HasToString Object>
std::string makeString(Object&& object) 
{
    return std::forward<Object>(object).to_string();
}

template <IsString String>
std::string makeString(String&& s) 
{
    return std::string(std::forward<String>(s));
}

template <IsContainer Iterable>
std::string makeString(Iterable&& iterable) 
{
    std::string result;
    for (auto&& i : iterable)
    {
        if (!result.empty())
            result += ';';

        // a constexpr if, so the compiler can omit unused branch and
        // allow non-copyable types usage
        if constexpr (std::is_rvalue_reference_v<decltype(iterable)>)
            result += makeString(std::move(i));
        else 
            result += makeString(i);
    }
    return result;
}

template <typename... Pack>
    requires (sizeof...(Pack) > 1)
std::string makeString(Pack&&... pack)
{
    return (... += makeString(std::forward<Pack>(pack)));
}
// main.cpp
#include <iostream>
#include <vector>
#include <set>
#include "makeString.hpp"

struct A
{
    std::string to_string() const { return "A"; }
};

struct B
{
    int m_i = 0;
    std::string to_string() const { return "B{" + std::to_string(m_i) + "}"; }
};

struct NonCopyable
{
    std::string m_s;
    NonCopyable(const char* s) : m_s(s)  {}
    NonCopyable(NonCopyable&&) = default;
    NonCopyable(const NonCopyable&) = delete;

    std::string   to_string() const &  { return m_s; }
    std::string&& to_string() &&       { return std::move(m_s); }
};

struct C
{
    std::string m_string;
  
    auto begin() const { return std::begin(m_string); }
    auto begin()       { return std::begin(m_string); }
    auto end() const   { return std::end(m_string); }
    auto end()         { return std::end(m_string); }

    std::string to_string() const { return "C{\"" + m_string + "\"}"; }
};

template <typename Container>
    requires IsContainer<Container> && HasToString<Container>
std::string makeString(Container&& c)
{
    return std::forward<Container>(c).to_string();
}

int main()
{
    A a;
    B b = {1};

    const std::vector<int> xs = {1, 2, 3};
    const std::set<float> ys = {4, 5, 6};
    const double zs[] = {7, 8, 9};

    std::cout << "a: " << makeString(a) << "; b: " << makeString(b) 
              << "; pi: " << makeString(3.1415926) << std::endl
              << "xs: " << makeString(xs) << "; ys: " << makeString(ys) 
              << "; zs: " << makeString(zs)
              << std::endl;

    std::cout << makeString("Hello, ") 
              << makeString(std::string_view("world")) 
              << makeString(std::string("!!1")) 
              << std::endl;

    auto makeVector = []()
    { 
        std::vector<NonCopyable> v;
        v.emplace_back("two ");
        v.emplace_back(" non-copyables");
        return v; 
    };

    std::cout << makeString(makeVector())
              << std::endl
              << makeString( C { "a container with its own to_string()" } )
              << std::endl;

    std::cout << makeString("a ", std::string_view("variadic "), std::string("with a double: "), 3.14)
              << std::endl;
}

输出为:

a: A; b: B{1}; pi: 3.141593
xs: 1;2;3; ys: 4.000000;5.000000;6.000000; zs: 7.000000;8.000000;9.000000
Hello, world!!1
two ; non-copyables
C{"a container with its own to_string()"}
a variadic with a double: 3.140000

参考文献:

【Claude】Not logged in 错误:未登录状态的判断与 /login 认证路径 bug报错已解决 login 是首选修复:大多数 "Not logged in" 只需重新执行 /login检查系统时间:时间偏差是 Token 验证失败的常见原因避免密钥库被清理:将 Claude Code 添加到系统清理工具的白名单企业环境用 API Key:如果组织禁用 OAuth,使用 API Key 认证区分凭证类型:理解 Not logged in(无凭证)vs Invalid key(有但无效) 阅读详情

相关推荐

三菱 FX5U PLC 4轴程序 控制松下伺服3个, 步进电机一个

三菱 FX5U PLC 4轴程序。 控制松下伺服3个, 步进电机一个, 四轴自动堆垛码垛设备程序, 回原点动作用专用的原点回归指令写的, 手动运行用三菱相对定位指令写的 , 自动运行用绝对定位指令写的取料运行, 表格定位写的自动放料运行, 设备各种报警异常都有写出 程序每一步都有注释。 包括PLC程序、显控触摸屏 程序、松下伺服驱动器接线图。 程序内都有IO注释 编号:4816635519484681汇美0808 ...

m0_68069172的博客 3381

C++ 模板:以简御繁-1/5

英文原文:这篇系列文章从一个简单的需求开始,介绍了如何一步一步,从简到繁用模板来实现,仔细阅读,你一定会有收获。想象一下有一种能力将所有内容转换为字符串:整数、向量、布尔值 - 所有这些都被转换成一条信息,以输出到我们想要的任何地方。这个要求看起来很方便,也不太难,所以让我们深入研究它——这是我为我的宠物项目编写调试变量观察器时的第一个想法。尽管期望让事情变得简单可能会导致几个不眠之夜,但我还是鼓励读者通过基本的模板编程概念踏上这段旅程。

immigrator的专栏 69

深入构建加密货币价格预测系统:从数据获取到模型优化的完整指南!

🏆本文收录于「编程与技术实战」专栏,此专栏涵盖了C/C++编程、人工智能、数据结构、机器学习等技术领域的内容,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

**My Coding Family** 1276

C++ 模板:以简御繁-4/5

Concept用法

immigrator的专栏 86

保姆级教程:用OpenCV的calibrateHandEye()搞定机器人手眼标定(附Python代码)

本文详细介绍了如何使用OpenCV的calibrateHandEye()函数完成机器人手眼标定,解决机械臂与相机坐标系对齐问题。教程涵盖从基础原理、数据采集到Python代码实现的完整流程,特别适合需要精确视觉引导的机器人应用开发。通过手眼标定,可确保机械臂准确抓取相机检测到的目标物体。

weixin_33712987的博客 515

【git】win 10 / win 11:git 首次拉取云效平台代码详细教程

git 首次拉取云效平台代码详细教程:Git 生成 SSH 公钥、云效工作台添加 SSH 公钥、git 使用 SSH / HTTP 链接拉取云效代码

顾三殇 —— 博客空间(软件测试) 4215

C++ 模板:以简御繁-2/5

Type traits用法

immigrator的专栏 93

微信小程序|基于小程序实现定位打卡功能

此文主要在小程序内实现打卡功能,可根据用户位置与公司设定的打卡范围实时判断打卡是否合法,并在后台对打卡记录进行管理。

商务合作 / 项目定制 / 学习交流。个人vx:lovely_wml 1万+

C++ 模板:以简御繁-3/5

完美转发

immigrator的专栏 74

芯片设计|SoC芯片设计流程 与 软硬件协同设计

SoC通常被称作系统级芯片或者片上系统,作为一个完整的系统,**其包含了硬件和软件两部分内容**。这里所说的硬件指SoC芯片部分,**软件是指运行在SoC芯片上的系统及应用程序**。既然它是由软件和硬件组合而成,则在进行系统设计时,**就必须同时从软件和硬件的角度去考虑。**

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 1821

15、降维算法:原理、实现与应用

本文深入介绍了多种降维算法,包括线性方法如主成分分析(PCA)和线性判别分析(LDA),以及非线性方法如Sammon映射、t分布随机邻域嵌入(t-SNE)和自编码器。文章通过C++库Dlib和Shogun展示了这些算法的具体实现,并结合图像压缩和数据可视化等实际案例,分析了不同算法的适用场景。此外,还探讨了降维算法的选择策略、优化改进方法以及未来发展趋势,为读者提供了全面的技术理解和应用指导。

lemon的博客 115

掌握C++模板编程:Cpp-Primer-5th-Exercises第16章实战指南

C++模板编程是C++ Primer第5版第16章的核心内容,也是提升C++编程能力的关键技能。通过Cpp-Primer-5th-Exercises项目中的实战练习,你将系统掌握模板类、函数模板、模板特化等核心概念,轻松应对复杂的泛型编程挑战。 ## 为什么模板编程是C++开发者的必备技能 🚀 模板是C++实现泛型编程的基础,它允许你编写与类型无关的代码,大幅提高代码复用性和灵活性。在标准库

gitblog_00320的博客 935

170、电机控制中的温升测试

本文以一次IGBT炸管事故为切入点,深入剖析电机控制中温升测试的实质:不仅是温度测量,更是对铜损、铁损、散热效率等热管理问题的系统验证。作者强调测试前热电偶布置、环境记录、预运行的重要性,提出实时监控电流谐波、绕组电阻、散热梯度等关键参数,并通过损耗分离与冷却系统实测,揭示真实热源。结合温升曲线异常分析与经验建议,强调留余量、仿真结合实测、关注瞬态工况等实践要点,警示温升测试是保障可靠性与安全性的核心环节。

2401_88858637的博客 47

C++模板(函数模板/类模板)

Ⅰ、编程 Ⅱ、函数模板 1、函数模板概念 2、函数模板格式 3、函数模板的原理 4、函数模板的实例化 1.隐式实例化 2.显示实例化 5、模板参数的匹配原则 ​编辑补充:函数不一定都能推演,但是类模板一定要指定 Ⅲ、类模板 1、类模板的定义格式 2、类模板的实例化 3、类外定义类模板参数 4、模板的分离编译......

qq_61386381的博客 1万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

RedPanda-CPP项目模板使用技巧:快速创建专业C/C++工程

RedPanda-CPP是一款基于Qt开发的轻量级C/C++集成开发环境(IDE),它提供了丰富的项目模板功能,帮助开发者快速搭建专业的C/C++工程。本文将详细介绍如何充分利用RedPanda-CPP的项目模板,提升开发效率,让你轻松创建规范的C/C++项目。 ## 为什么使用项目模板? 项目模板是RedPanda-CPP的核心功能之一,它能够: - 提供标准化的项目结构,确保代码组织一致性

gitblog_01423的博客 862

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

【C++】模板进阶:深入解析模板特化

本章将深入探讨模板的高级应用,重点解析模板特化的概念和实现方法。

专注于 C/C++ 后端开发,涵盖基础语法、Linux系统与网络、MySQL数据库、常见算法及项目经验。 2519

【C++】—— 模板进阶

C++模板

yusjushd的博客 2645

【CPP】C++模板:初阶到进阶语法与实用编程示例

探索C++模板的深度与广度,《C++模板:初阶到进阶语法与实用编程示例》带您从基础语法起步,逐步深入到高级特性。本博客结合精炼的理论知识和丰富的代码实例,揭示模板编程的精髓,助您提升开发效率,优化代码结构。无论您是初学者还是资深开发者,都能在这里找到提升C++编程技能的宝贵资源。

BinaryWiker的博客 1380

C++模板—函数模板、类模板

C++函数模板、类模板

Han同学 1672

5个高效配置让Dev-CPP成为C/C++编程入门利器

Dev-CPP是一款轻量级C/C++集成开发环境,通过优化的代码编辑界面、智能补全功能和便捷的项目管理系统,为编程学习者和初级开发者提供了高效的开发体验。该工具在经典Dev-C++基础上进行了深度重构,保留了轻量特性的同时增强了现代化开发功能,特别适合C/C++初学者快速掌握编程基础。 ## 一、基础配置篇:从零开始搭建开发环境 ### 编译器安装与验证 **问题现象**:首次启动程序时出现

gitblog_00692的博客 331

【C++】C++11可变参数模板(函数模板、类模板)

在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。 本文实例源码github地址:https://github.com/yngzMiao/yngzmiao-blogs/tree/master/2020Q2/20200401...

Yngz_Miao的博客 3万+

MemPalace 接入 Cursor 实战指南:hooks.json 配置、一键安装与自动记忆钩子管线

MemPalace 是一个开源 AI 记忆系统,通过把会话内容"存入宫殿"(palace)来为 Cursor、Claude Code 等编码代理提供跨会话的持久记忆。本文围绕仓库中 [examples/cursor/README.md](https://link.gitcode.com/i/920643c4e43c05b69a80c502e7c01aa3) 及配套的 [examples/curso

gitblog_01057的博客 452

【C++模板进阶】——【非类型模板参数 / 模板的特化 / 模板分离编译】

本文深入探讨C++模板编程的两个重要特性:非类型模板参数和模板特化。非类型模板参数允许在编译期指定常量值,用于创建固定大小的数组等场景。

枫亭湖区的博客 3220

Dev-Cpp5.11版本Dev-c安装包:编程利器,轻松掌握C/C++开发

Dev-Cpp5.11版本Dev-c安装包:编程利器,轻松掌握C/C++开发 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 Dev-Cpp 5.11版本安装包为您提供了一种简单快捷的方式来安装和使用Dev-C++。Dev-C++(又称Dev-Cpp)是一款广受欢迎的免费集成开发环境(IDE),它专为C/C++编程语言而设计。这个安装包包含了2020年5月的最新版Dev...

gitblog_06729的博客 995

终极指南:如何用Dev-CPP快速上手C/C++开发

你是否正在寻找一款轻量级但功能强大的C/C++集成开发环境?Dev-CPP正是为你量身打造的解决方案。这个开源项目不仅完全免费,还提供了从代码编辑到编译调试的一站式开发体验,特别适合初学者和中小型项目开发。在接下来的内容中,我将带你深入了解这个高效IDE的方方面面。 ## 🚀 快速入门:你的第一个C++程序 ### 为什么选择Dev-CPP作为起点? 对于C/C++新手来说,选择一个合适的开

gitblog_00667的博客 796

黑马C++笔记——模板(CPP)

C++模板的常见操作

感觉画质不如...原神的博客 986

[C/C++]浅析C++中的模板

本文将对C++中的模板进行简单介绍,明确一些基本概念。 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。 模板是创建泛型类或函数的蓝图或公式。

RMA515T的博客 2万+
上一篇: 在C++模板中,设置一个无名模板参数的默认值为0到底是什么含义
下一篇: C++ 模板:以简御繁-1/5
SONG_CA
博客等级 码龄24年 74粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值