2017-07-24 42 views
2

我無法找到關於如何在編譯時在現代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} 

我會很高興與任何有點容易閱讀,例如,

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 

有沒有辦法?我能做些什麼來解決這個問題?

回答

3

如果您正在使用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); 

它不工作,C++ 14,因爲std::array不constexpr友好直到C++ 17。但是,如果你不在乎最後的結果是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); 

編譯器將幾乎肯定優化concat路程。

如果你需要一個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); 
+0

也許這是可能的。你建議如何使它適合兩個以上的陣列? –

+1

注意'3'可以是'one_elem.size()+ two_elem.size()',因爲'array :: size'是'constexpr'。 – vsoftco

+2

@vsoftco你可以隨時聲明它爲'constexpr auto all = ...' – Justin

4

有已經來連接在C數組++道:std::tuple_cat 。唯一的問題是它給你一個tuple<uint8_t, uint8_t, uint8_t>而不是std::array<uint8_t, 3>。但是這個問題可以通過不同的標準庫函數來解決:std::apply。那是技術上的C++ 17,但是可以在C++ 14中實現。你只需要一個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{}; 

,然後你可以使用它:

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

這可能是更容易只是藏在一個功能背後:

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)...)); 
} 

完美轉發與lambda表達式是有點醜陋。 Target類型允許用戶指定結果數組的類型 - 否則將被選爲第一個元素的衰減類型。

+0

這種方法的一個非常強大的好處是它是通用的:它不僅僅適用於'std :: array'。你可以'array_concat'一個'元組'和一個'array '就好了。它也容易variadic,因爲它使用'std :: tuple_cat' – Justin

+0

我喜歡'std :: forward'永遠不是更簡單的方式來轉發東西 –

+0

@PserserBy感謝修復! – Barry

相關問題