c ++ 11 constexpr 将 std::array 列表扁平化为数组 [英] c++11 constexpr flatten list of std::array into array

查看:59
本文介绍了c ++ 11 constexpr 将 std::array 列表扁平化为数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从 c++11 开始,constexpr 和模板元编程似乎是在微型微控制器上节省稀缺内存的好方法.

I am beginning with c++11, constexpr and template metaprogramming seems a nice way to save scarce ram on tiny microcontroler.

有没有办法写一个模板来展平constexpr数组的列表,什么我需要的是一种方法:

Is there a way to write a template to flatten a list of constexpr array, what I need is a way to do :

constexpr std::array<int, 3> a1 = {1,2,3};
constexpr std::array<int, 2> a2 = {4,5};
constexpr auto a3 = make_flattened_array (a1,a2);

我使用 gcc 4.8.4 (arm-none-eabi),如果需要,可以使用 std=c++11 或 c++1y 选项进行编译.

I use gcc 4.8.4 (arm-none-eabi), and can compile with std=c++11 or c++1y option if it is needed.

推荐答案

注意 - 我对您的问题的理解如下:您想连接这两个数组并将结果展平为一个包含其元素的串联.

您可以使用三个 C++11+ 概念来实现您的目标:

You can accomplish your goal with three C++11+ concepts:

  1. 可变模板
  2. constexpr 表达式
  3. 参数包

您首先创建一个模板(一个空壳)来开始设计您的递归时尚列表扁平化函数:

You start by creating a template (an empty shell) to start designing your recursive-fashion list flattening function:

template<unsigned N1, unsigned N2>
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
  // TODO
}

到目前为止一切顺利:constexpr 说明符将提示编译器在每次可能的时候在编译时评估该函数.

so far so good: the constexpr specifier will hint the compiler to compile-time evaluate that function each time it can.

现在是有趣的部分:std::array 有(因为 c++1y) 一个 constexpr 重载运算符[],这意味着你可以写一些像

Now for the interesting part: std::array has (since c++1y) a constexpr overload for the operator[], this means you can write something like

template<unsigned N1, unsigned N2>
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
  return std::array<int,N1+N2>{a1[0],a1[1],a1[2],a2[0],a2[1]};
}

(注意 aggregate-initialization 从一系列初始化对象整数值)

(notice the aggregate-initialization to initialize the object from a series of integer values)

显然,手动硬编码对两个数组的值的所有索引访问并不比仅仅声明连接数组本身更好.节省一天的概念如下:参数包.模板参数包是接受 0 个或多个模板参数的模板参数.具有至少一个参数包的模板称为可变参数模板.

Obviously manually hard-coding all the index accesses to the values of the two arrays is no better than just declaring the concatenated array itself. The concept that will save the day is the following: Parameter Packs. A template parameter pack is a template parameter that accepts 0 or more template arguments. A template with at least one parameter pack is called variadic template.

很酷的事情是将参数包扩展到指定位置的能力,例如:

The cool thing is the ability of expanding the parameter pack into specified locations like:

#include <iostream>
#include <array>

template<unsigned... Num>
std::array<int, 5> function(const std::array<int,5>& source) {
    return std::array<int,5>{source[Num]...};
}


int main() {
    std::array<int,5> source{7,8,9,10,11};
    std::array<int,5> res = function<0,1,2,3,4>(source);

    for(int i=0; i<res.size(); ++i)
        std::cout << res[i] << " "; // 7 8 9 10 11

    return 0;
}

所以我们现在唯一需要的是能够在编译时生成索引系列",如

So the only thing we need right now is to be able to compile-time generate the "index series" like

std::array<int,5> res = function<0,1,2,3,4>(source);
                                 ^ ^ ^ ^ ^

在这一点上,我们可以再次利用参数包与继承机制结合使用:想法是具有 derived : base : other_base : another_base : ... 的深层嵌套层次结构将索引累积"到参数包中并在索引达到 0 时终止递归"的类.如果您不理解上一句,请不要担心,请看以下示例:

At this point we can again take advantage of the parameter packs in conjunction with an inheritance mechanism: the idea is to have a deeply nested hierarchy of derived : base : other_base : another_base : ... classes which would "accumulate" the indices into the parameter pack and terminate the "recursion" when the index reaches 0. If you didn't understand the previous sentence don't worry and take a look at the following example:

std::array<int, 3> a1{42,26,77};

// goal: having "Is" = {0,1,2} i.e. a1's valid indices
template<unsigned... Is> struct seq;

我们可以通过以下方式生成索引序列:

we can generate a sequence of indices in the following way:

template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, Is...>{}; // each time decrement the index and go on
template<unsigned... Is>
struct gen_seq<0 /*stops the recursion*/, Is...> : /* generate the sequence */seq<Is...>{};

std::array<int, 3> a1{42,26,77};
gen_seq<3>{};

无论如何都缺少一些东西:上面的代码将以 gen_seq<3, (nothing)> 开始并实例化指定的模板,该模板将实例化 gen_seq<2, (nothing)> 作为将实例化 gen_seq<1 的基类, (nothing)> 作为将 gen_seq<0 实例化的基类,(nothing)> 作为将 seq<(nothing)> 实例化为最终序列的基类.

There's something missing anyway: the code above will start with gen_seq<3, (nothing)> and instantiate the specified template which will instantiate the gen_seq<2, (nothing)> as its base class that will instantiate the gen_seq<1, (nothing)> as its base class that will instantiate the gen_seq<0, (nothing)> as its base class that will instantiate the seq<(nothing)> as final sequence.

序列是'(nothing)',有问题..

The sequence is '(nothing)', something is wrong..

为了将索引累积"到参数包中,您需要在每次递归时将减少的索引的副本"添加到参数包中:

In order to "accumulate" the indices into the parameter pack you need to "add a copy" of the decreased index to the parameter pack at each recursion:

template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, /*This copy goes into the parameter pack*/ N-1, Is...>{};

template<unsigned... Is>
struct gen_seq<0 /*Stops the recursion*/, Is...> : /*Generate the sequence*/seq<Is...>{};
template<unsigned... Is> struct seq{};

// Using '/' to denote (nothing)
gen_seq<3,/> : gen_seq<2, 2,/> : gen_seq<1,  1,2,/> : gen_seq<0, 0,1,2,/> : seq<0,1,2,/> .

所以现在我们能够将所有的片段重新收集在一起并生成两个索引序列:一个用于第一个数组,一个用于第二个数组,并将它们连接在一起形成一个新的返回数组,该数组将保存连接和扁平的联合两个数组(比如将它们附加在一起).

so now we're able to recollect all the pieces together and generate two sequences of indices: one for the first array and one for the second array and concatenate them together into a new return array which will hold the concatenated and flattened union of the two arrays (like appending them together).

此时,以下代码应该很容易理解:

The following code, at this point, should be easily comprehensible:

#include <iostream>
#include <array>

template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

template<unsigned N1, unsigned... I1, unsigned N2, unsigned... I2>
// Expansion pack
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2, seq<I1...>, seq<I2...>){
  return { a1[I1]..., a2[I2]... };
}

template<unsigned N1, unsigned N2>
// Initializer for the recursion
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
  return concat(a1, a2, gen_seq<N1>{}, gen_seq<N2>{});
}

int main() {
    constexpr std::array<int, 3> a1 = {1,2,3};
    constexpr std::array<int, 2> a2 = {4,5};

    constexpr std::array<int,5> res = concat(a1,a2);
    for(int i=0; i<res.size(); ++i)
        std::cout << res[i] << " "; // 1 2 3 4 5

    return 0;
}

http://ideone.com/HeLLDm

参考文献:

https://stackoverflow.com/a/13294458/1938163

http://en.cppreference.com/

http://en.wikipedia.org

这篇关于c ++ 11 constexpr 将 std::array 列表扁平化为数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆