简体   繁体   English

在编译时将两个或多个不同大小的数组合并到一个数组中

[英]Combine two or more arrays of different size to one array at compiletime

I was not able to find an answer on how to combine two or more arrays at compiletime in modern c++. 我无法在现代c ++中找到有关如何在编译时组合两个或多个数组的答案。

#include <array>
#include <cstdint>

const std::array<std::uint8_t, 1> one_elem = {1};
const std::array<std::uint8_t, 2> two_elem = {2, 3};
const std::array<std::uint8_t, 3> all = {one_elem, two_elem}; 
// expected: all == {1, 2, 3}

I would be glad with anything that is somewhat easy to read, eg 我会很高兴看到任何有点容易阅读的内容,例如

std::uint8_t one_elem[] = {1};
std::uint8_t two_elem[] = {2, 3};
std::uint8_t all[] = {one_elem, two_elem}; // cannot be that hard

Is there a way? 有办法吗? What can I do to get this solved? 我该怎么做才能解决这个问题?

If you are using C++17, you can do this: 如果您使用的是C ++ 17,则可以执行以下操作:

template <typename T, std::size_t N1, std::size_t N2>
constexpr std::array<T, N1 + N2> concat(std::array<T, N1> lhs, std::array<T, N2> rhs)
{
    std::array<T, N1 + N2> result{};
    std::size_t index = 0;

    for (auto& el : lhs) {
        result[index] = std::move(el);
        ++index;
    }
    for (auto& el : rhs) {
        result[index] = std::move(el);
        ++index;
    }

    return result;
}

constexpr std::array<std::uint8_t, 1> one_elem = {1};
constexpr std::array<std::uint8_t, 2> two_elem = {2, 3};
constexpr std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);

It doesn't work in C++14, because std::array isn't constexpr friendly until C++17. 它在C ++ 14中不起作用,因为在C ++ 17之前std::array不是constexpr友好的。 However, if you don't care that the final result is constexpr , you can simply mark each variable as const , and this will work: 但是,如果您不关心最终结果是constexpr ,您可以简单地将每个变量标记为const ,这将起作用:

const std::array<std::uint8_t, 1> one_elem = {1};
const std::array<std::uint8_t, 2> two_elem = {2, 3};
const std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);

The compiler will almost certainly optimize the concat away. 编译器几乎肯定会优化concat

If you need a C++14 solution, we have to create it via std::array 's constructor, so it's not nearly as nice: 如果你需要一个C ++ 14解决方案,我们必须通过std::array的构造函数创建它,所以它不是那么好:

#include <array>
#include <cstdint>
#include <cstddef>
#include <type_traits>

// We need to have two parameter packs in order to
// unpack both arrays. The easiest way I could think of for
// doing so is by using a parameter pack on a template class
template <std::size_t... I1s>
struct ConcatHelper
{
    template <typename T, std::size_t... I2s>
    static constexpr std::array<T, sizeof...(I1s) + sizeof...(I2s)>
    concat(std::array<T, sizeof...(I1s)> const& lhs,
           std::array<T, sizeof...(I2s)> const& rhs,
           std::index_sequence<I2s...>)
    {
        return { lhs[I1s]... , rhs[I2s]... };
    }
};

// Makes it easier to get the correct ConcatHelper if we know a
// std::index_sequence. There is no implementation for this function,
// since we are only getting its type via decltype()
template <std::size_t... I1s>
ConcatHelper<I1s...> get_helper_type(std::index_sequence<I1s...>);

template <typename T, std::size_t N1, std::size_t N2>
constexpr std::array<T, N1 + N2> concat(std::array<T, N1> const& lhs, std::array<T, N2> const& rhs)
{
    return decltype(get_helper_type(std::make_index_sequence<N1>{}))::concat(lhs, rhs, std::make_index_sequence<N2>{});
}

constexpr std::array<std::uint8_t, 1> one_elem = {1};
constexpr std::array<std::uint8_t, 2> two_elem = {2, 3};
constexpr std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);

There's already a way to concatenate arrays in C++: std::tuple_cat . 已经有一种方法可以在C ++中连接数组: std::tuple_cat The only problem is that it gives you a tuple<uint8_t, uint8_t, uint8_t> instead of a std::array<uint8_t, 3> . 唯一的问题是它给你一个tuple<uint8_t, uint8_t, uint8_t>而不是std::array<uint8_t, 3> But that problem is solvable with a different standard library function: std::apply . 但是这个问题可以通过不同的标准库函数来解决: std::apply That one is technically C++17, but is implementable in C++14. 那个技术上是C ++ 17,但是可以在C ++ 14中实现。 You just need a funject: 你只需要一个funject:

struct to_array_t {
    template <class T, class... Ts> 
    std::array<std::decay_t<T>, sizeof...(Ts)+1> operator()(T&& t, Ts&&... ts) const {
        return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
    }   
} to_array{};

and then you can use it: 然后你可以使用它:

auto all = std::apply(to_array, std::tuple_cat(one_elem, two_elem));

Which might be easier to just hide behind a function: 这可能更容易隐藏在一个函数后面:

template <class Target=void, class... TupleLike>
auto array_concat(TupleLike&&... tuples) {
    return std::apply([](auto&& first, auto&&... rest){
        using T = std::conditional_t<
            !std::is_void<Target>::value, Target, std::decay_t<decltype(first)>>;
        return std::array<T, sizeof...(rest)+1>{{
            decltype(first)(first), decltype(rest)(rest)...
        }};
    }, std::tuple_cat(std::forward<TupleLike>(tuples)...));
}

Perfect forwarding with lambdas is a bit ugly. 使用lambdas完美转发有点难看。 The Target type is allow the user to specify a type for the resulting array - otherwise it will be selected as the decayed type of the first element. Target类型允许用户为结果数组指定类型 - 否则它将被选为第一个元素的衰减类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM