前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 模板黑魔法 —— 编译期序列与 std::tuple 遍历

C++ 模板黑魔法 —— 编译期序列与 std::tuple 遍历

作者头像
Kindem
发布2022-08-12 14:16:01
1.1K0
发布2022-08-12 14:16:01
举报
文章被收录于专栏:Kindem的博客Kindem的博客

编译期序列

最近看到一个很有意思的模板写法:

代码语言:javascript
复制
template <size_t... S>
struct IndexSequence {};

template <size_t N, size_t... S>
struct IndexSequenceMaker : public IndexSequenceMaker<N - 1, N - 1, S...> {};

template <size_t... S>
struct IndexSequenceMaker<0, S...> {
    using Type = IndexSequence<S...>;
};

template <size_t N>
using MakeIndexSequence = typename IndexSequenceMaker<N>::Type;

乍一看啥玩意儿,仔细看会发现它的作用是生成一个编译期序列,如:

代码语言:javascript
复制
// IndexSequence<0, 1, 2, 3, 4>
MakeIndexSequence<5>

它的实现非常巧妙,我们以上面这个例子为切入点,按照它的思路去展开模板:

代码语言:javascript
复制
template <>
struct IndexSequenceMaker<0, 0, 1, 2, 3, 4> {
    using Type = IndexSequence<0, 1, 2, 3, 4>;
}

template <>
struct IndexSequenceMaker<1, 1, 2, 3, 4> : public IndexSequenceMaker<0, 0, 1, 2, 3, 4> {}

template <>
struct IndexSequenceMaker<2, 2, 3, 4> : public IndexSequenceMaker<1, 1, 2, 3, 4>;

template <>
struct IndexSequenceMaker<3, 3, 4> : public IndexSequenceMaker<2, 2, 3, 4>;

template <>
struct IndexSequenceMaker<4, 4> : public IndexSequenceMaker<3, 3, 4>;

template <>
struct IndexSequenceMaker<5> : public IndexSequenceMaker<4, 4> {}

template <>
using MakeIndexSequence<5> = typename IndexSequenceMaker<5>::Type;

秒懂了,利用继承关系来传递不断生成的序列可变参,最后以 N = 0 的特化来终止生成。

使用编译期序列来做 std::tuple 遍历

编译期序列最大的作用就是用于 std::tuple 的遍历,下面是一段 c++ 11 的代码:

代码语言:javascript
复制
template <size_t... S>
struct IndexSequence {};

template <size_t N, size_t... S>
struct IndexSequenceMaker : public IndexSequenceMaker<N - 1, N - 1, S...> {};

template <size_t... S>
struct IndexSequenceMaker<0, S...> {
    using Type = IndexSequence<S...>;
};

template <size_t N>
using MakeIndexSequence = typename IndexSequenceMaker<N>::Type;

template <typename T, typename F>
void ForEachTuple(T&& tuple, F&& consumer)
{
    ForEachTupleInternal(std::forward<T>(tuple), std::forward<F>(consumer), MakeIndexSequence<std::tuple_size<T>::value> {});
}

template <typename T, typename F, size_t... S>
void ForEachTupleInternal(T&& tuple, F&& consumer, IndexSequence<S...>)
{
    std::initializer_list<int> { (consumer(std::get<S>(tuple)), 0)... };
}

struct Consumer {
    template <typename T>
    void operator()(T&& value)
    {
        std::cout << value << std::endl;
    }
};

int main(int argc, char* argv[])
{
    ForEachTuple(std::make_tuple(1, 2.1, "Hello"), Consumer {});
    return 0;
}

代码很简单,这里要注意的就是 std::get<>()... 的配合来不断消费 std::tuple 的元素,最后用 std::initializer_list<int> 来接收可变参防止编译错误。

值得一提的是,c++ 14 已经内置了编译期序列,如果项目能支持到 c++ 14,则可以直接这么写:

代码语言:javascript
复制
template <typename T, typename F>
void ForEachTuple(T&& tuple, F&& consumer)
{
    // c++ 14 的 make_index_sequence
    ForEachTupleInternal(std::forward<T>(tuple), std::forward<F>(consumer), std::make_index_sequence<std::tuple_size<T>::value> {});
}

template <typename T, typename F, size_t... S>
void ForEachTupleInternal(T&& tuple, F&& consumer, std::index_sequence<S...>)
{
    std::initializer_list<int> { (consumer(std::get<S>(tuple)), 0)... };
}

int main(int argc, char* argv[])
{
    // c++ 14 的 lambda 中 auto 作为参数
    ForEachTuple(std::make_tuple(1, 2.1, "Hello"), [](const auto& value) -> void { std::cout << value << std::endl; });
    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 编译期序列
  • 使用编译期序列来做 std::tuple 遍历
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档