现代C++实战30讲
吴咏炜
前 Intel 资深软件架构师
立即订阅
3995 人已学习
课程目录
已更新 33 讲 / 共 33 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | C++这么难,为什么我们还要用C++?
免费
课前必读 | 有关术语发音及环境要求
基础篇 (9讲)
01 | 堆、栈、RAII:C++里该如何管理资源?
02 | 自己动手,实现C++的智能指针
03 | 右值和移动究竟解决了什么问题?
04 | 容器汇编 I:比较简单的若干容器
05 | 容器汇编 II:需要函数对象的容器
06 | 异常:用还是不用,这是个问题
07 | 迭代器和好用的新for循环
08 | 易用性改进 I:自动类型推断和初始化
09 | 易用性改进 II:字面量、静态断言和成员函数说明符
提高篇 (11讲)
10 | 到底应不应该返回对象?
11 | Unicode:进入多文字支持的世界
12 | 编译期多态:泛型编程和模板入门
13 | 编译期能做些什么?一个完整的计算世界
14 | SFINAE:不是错误的替换失败是怎么回事?
15 | constexpr:一个常态的世界
16 | 函数对象和lambda:进入函数式编程
17 | 函数式编程:一种越来越流行的编程范式
18 | 应用可变模板和tuple的编译期技巧
19 | thread和future:领略异步中的未来
20 | 内存模型和atomic:理解并发的复杂性
实战篇 (7讲)
21 | 工具漫谈:编译、格式化、代码检查、排错各显身手
22 | 处理数据类型变化和错误:optional、variant、expected和Herbception
23 | 数字计算:介绍线性代数和数值计算库
24 | Boost:你需要的“瑞士军刀”
25 | 两个单元测试库:C++里如何进行单元测试?
26 | Easylogging++和spdlog:两个好用的日志库
27 | C++ REST SDK:使用现代C++开发网络应用
新年特别策划 (2讲)
新春寄语 | 35年码龄程序员:人生漫长,走点弯路在所难免
新春福利 | C++好书荐读
未来篇 (2讲)
28 | Concepts:如何对模板进行约束?
29 | Ranges:无迭代器的迭代和更方便的组合
现代C++实战30讲
登录|注册

18 | 应用可变模板和tuple的编译期技巧

吴咏炜 2020-01-06
你好,我是吴咏炜。
今天我们讲一个特殊的专题,如何使用可变模板和 tuple 来完成一些常见的功能,尤其是编译期计算。

可变模板

可变模板 [1] 是 C++11 引入的一项新功能,使我们可以在模板参数里表达不定个数和类型的参数。从实际的角度,它有两个明显的用途:
用于在通用工具模板中转发参数到另外一个函数
用于在递归的模板中表达通用的情况(另外会有至少一个模板特化来表达边界情况)
我们下面就来分开讨论一下。

转发用法

以标准库里的 make_unique 为例,它的定义差不多是下面这个样子:
template <typename T,
typename... Args>
inline unique_ptr<T>
make_unique(Args&&... args)
{
return unique_ptr<T>(
new T(forward<Args>(args)...));
}
这样,它就可以把传递给自己的全部参数转发到模板参数类的构造函数上去。注意,在这种情况下,我们通常会使用 std::forward,确保参数转发时仍然保持正确的左值或右值引用类型。
稍微解释一下上面三处出现的 ...
typename... Args 声明了一系列的类型——class...typename... 表示后面的标识符代表了一系列的类型。
Args&&... args 声明了一系列的形参 args,其类型是 Args&&
forward<Args>(args)... 会在编译时实际逐项展开 Argsargs ,参数有多少项,展开后就是多少项。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《现代C++实战30讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(6)

  • hello world
    compose那是完全没看懂唉,还有sequence那...

    作者回复: 给个提示,到下面这个网站上看看模板是如何展开的:

    https://cppinsights.io/

    2020-01-09
    1
  • 三味
    17和18讲可以说,真是劝退不少非c++读者吧。。。
    个人认为,如果不是需要写库,这两节的内容应该用得也不多吧。
    作为一名图形工作者,我看这些东西,其实是因为好多图形库(说的就是你,CGAL)都是模板代码,看得眼疼。。。
    实用更优先。当然,理解这些这几讲的内容还是很有帮助的。
    顺便说一句,留言区贴代码实在是太费劲了。。。极客时间不好好搞搞Markdown回复格式么?好多问题还要贴代码的,有的就是挺长的。。
    试着回答一下问题:
    1. compose不带任何参数,
    template<class... Args>
    auto compose() {
    return [](auto&&... args) { return compose<Args...>(); };
    }
    关于不带参数的意义,我理解的是,没有参数,那么就没有要执行的操作,那么就什么都不执行,返回个空。再深挖掘我就想不到了。。。这里我是为了形式上的统一,返回了一个依然什么都不做的自身。

    2. 想不到其他方法。。要再预编译阶段就展开256个数值。。我还是等等答案吧。。

    3. 自己实现是不可能的。这辈子都不可能。之前我看别人的代码,有通过借助写了一个辅助的PushBack操作来实现。这个自然不是我原创,拿来主义:
    template <class T, T... Ints>
    struct integer_sequence {};

    template <size_t... Ints>
    using index_sequence = integer_sequence<size_t, Ints...>;

    template <size_t, typename T>
    struct push_back{};

    template <size_t N, size_t... Ints>
    struct push_back<N, index_sequence<Ints...>> {
    using type = index_sequence<Ints..., N>;
    };

    template <size_t N>
    struct index_sequence_helper {
    using type = typename push_back<N-1, typename index_sequence_helper<N-1>::type>::type;
    };

    template <>
    struct index_sequence_helper<1> {
    using type = index_sequence<0>;
    };

    template <size_t N>
    using make_index_sequence = typename index_sequence_helper<N>::type;

    作者回复: 如果极客时间要支持留言用 Markdown,恐怕也得是可选的,否则有些不用 Markdown 的就要晕菜了……倒是长度限制有点妨碍贴代码。

    1. 代码对。

    意义回头我再说。别人的代码我就不评价了。反正这个最后我会贴个参考答案。

    2020-01-10
  • 罗乾林
    1、看了老师的nvwa:
    inline auto compose()
    {
        return [](auto&& x) -> decltype(auto)
        {
            return std::forward<decltype(x)>(x);
        };
    }

    感觉还是为了使用方便,真的需要还可以这样:compose(std::identity{})

    第3题在网上查了些资料,发现很复杂。

    本节的思考题都好难,求老师解答。

    作者回复: 呃,compose() 返回的正是 identity(C++20 才有)啊。😉

    这讲的题目都会有个参考答案的。我会最后公布。

    2020-01-07
  • 禾桃
    template <typename F>
    auto compose(F f)
    {
        return [f](auto&&... x) {
            return f(x...);
        };
    }

    貌似用compiler(gcc version 4.8.5 20150623) 就会遇到下面编译错误
    // In function ‘auto compose(F)’:
    // error: expansion pattern ‘auto&&’ contains no argument packs
    // return [f](auto&&... x) {

    用compiler(gcc version 8.3.1 20190311)就不会有问题。

    如果公司目前只允许用(gcc version 4.8.5 20150623),请问有什么workaround?

    谢谢!

    作者回复: --- 更新 ---

    我费了九牛二虎之力,终于把例子改得能在 gcc 4.8 下工作了。我觉得你不想维护这样的代码的。

    这儿空间不够。我放在这里:

    http://wyw.dcweb.cn/download.asp?path=&file=jike_18_gcc48.cpp

    我觉得升级 GCC 绝对是更好的主意。

    --- 原回复 ---
    没啥好办法……泛型lambda至少要求gcc 4.9。只能不用这类功能了。

    手写一个函数对象模板也许可以完成这个功能(让 f 成为其数据成员)。你可以试试看。我暂时没时间试验。

    2020-01-07
  • 禾桃
    #1
    template <typename F,
              typename... Args>
    auto compose(F f, Args... args)
    {
      return [f, args...]() {
        return f(
          compose(args...)());
      };
    }


    template <typename F>
    auto compose(F f)
    {
        return f;
    }

    如果这些函数都在函数体内操作一个公用的数据,而且这些函数依次执行的顺序反应了一定的工程需求,那么就是有意义的吧。

    作者回复: 你这个compose带参数了呀……

    2020-01-07
  • 李亮亮
    N-->(N-1, N-1)-->(N-2, N-2, N-1)-->(1, 1 , 2 ....N-1)-->(0, 0, 1, 2...N-1)

    作者回复: 😁 学得挺快。

    2020-01-06
收起评论
6
返回
顶部