2016-06-18 9 views
2

は私がいないデフォルトコンストラクタを持つタイプAがあるとします。イニシャライザリストで簡単に作成できます:移入するのstdは::デフォルト以外の、構成可能型の配列(無可変引数テンプレート)

std::array<A, 5> arr = { 0, 1, 4, 9, 16 }; 

ここでパターンを確認できます。

int makeElement(size_t i) 
{ 
    return i * i; 
} 

std::array<A, 5> arr = { 
    makeElement(0), 
    makeElement(1), 
    makeElement(2), 
    makeElement(3), 
    makeElement(4) 
}; 

そして、はい、実際に私が(つまり64)の5つの要素よりもはるかに多くを持っている:はい、私は、配列の各値を計算するために発電機の機能を持たせることができます。ですから、makeElementを64回繰り返さないのはいいでしょう。私が思いついた唯一の解決策は、variadicテンプレートを使用して、パラメータパックをイニシャライザリストに展開することです:https://ideone.com/yEWZVq(すべてのコピーが適切に削除されているかどうかもチェックします)。この解決法は、this questionに触発されました。

それは動作しますが、私はこのような単純なタスクのための可変引数テンプレートを乱用しないようにしたいと思います。あなたは、実行可能なサイズを膨らませて、コンパイルを遅くすることを知っています。

  1. new
  2. 魔法std::arrayにストレージを変換する配置をループ内のすべての要素を初期化し、適切なサイズ
  3. といくつかの初期化されていないストレージを作成し、それを
を返す:私はこのような何かをしたいと思います

これをダイナミックメモリに実装するには、いくつか汚れたハックを行うことができます:https://ideone.com/tbw5lmしかし、これは全く問題がないstd::vectorよりも優れていません。

そして私は、私は自動メモリでそれを行うことができますどのようには考えています。私。フードの背後にあるすべてのもので、同じ便利な関数をstd::arrayという値で返します。何か案は?

私が思う、そのboost::container::static_vectorは私にとって良い解決策かもしれません。残念ながら、私はその特定の仕事のためにboostを使用することはできません。

PS。この質問は研究関心のようなものです。現実の世界では、可変テンプレートとstd::vectorの両方が問題なく動作します。私はちょうど何かを逃しているかもしれないことを知りたい。

+9

* "bloating executable size" *あなたはそれについては確かですか? –

+0

私はこれを多くの場所で行いますが、そのような初期化のために追加機能が必要になります。決定的な項目ではないが、それでもなお。この質問は、研究の関心事によく似ています。 – Mikhail

+1

'constexpr'ジェネレータを正しく使用すると、実行可能なサイズに影響しません。限り、コンパイルを遅くするように、まあ、C + +を歓迎します。 –

答えて

3

私は、コードの膨張に関するあなたの心配は誤解されていると思います。ここではサンプルです:

#include <utility> 
#include <array> 

template<std::size_t... ix> 
constexpr auto generate(std::index_sequence<ix...>) { 
    return std::array<int, sizeof...(ix)>{(ix * ix)...}; 
} 

std::array<int, 3> check() { 
return generate(std::make_index_sequence<3>()); 
} 

std::array<int, 5> glob = generate(std::make_index_sequence<5>()); 

それは非常にきちんとしたアセンブリを生成しません:

check(): 
     movl $0, -24(%rsp) 
     movl $1, -20(%rsp) 
     movl $4, %edx 
     movq -24(%rsp), %rax 
     ret 
glob: 
     .long 0 
     .long 1 
     .long 4 
     .long 9 
     .long 16 

ご覧のとおり、見えないコードの膨張を。静的配列は静的に初期化され、自動配列は単なる動きの束です。あなたが動きの束を恐ろしいコードが膨らんだと思うなら、それはループアンローリングと考えてください - 皆が大好きです!

ところで、他の適合するソリューションはありません。配列は、構築時に集約初期化を使用して初期化されるため、要素はデフォルトで構築可能または初期化される必要があります。ここで

+0

ニースの洞察力、ありがとう!しかし、最後の点については、完全に真実ではありません。私はプレースメントの新しい方法でそれをどうすることができるか説明の中にリンクを置いた。 – Mikhail

+0

@Mikhailあなたは新しいプレースメントを使用するように決められているようです。その場合は、おそらく 'std :: aligned_storage'を調べる必要があります。そうでなければ、あなたが作った自家製のソリューションは、あなたが知っていないものを実行した瞬間にクラッシュする可能性があります。 –

+0

@RichardHodges "あなたは新しいプレースメントを使うことに決めました" - 私はそうではありません。ここでは、「他の適合する解決策はありません」という記述が偽であることを示すためだけにここで言及しました。だからそこに他のいくつかのソリューションがあるかもしれません。 – Mikhail

2

は、発電機への入力の任意の範囲を可能にする別の方法です:

ここでは、ユースケースです:ここで

/// generate an integer by multiplying the input by 2 
/// this could just as easily be a lambda or function object 
constexpr int my_generator(int x) { 
    return 2 * x; 
} 

int main() 
{ 
    // generate a std::array<int, 64> containing the values 
    // 0 - 126 inclusive (the 64 acts like an end() iterator) 
    static constexpr auto arr = generate_array(range<int, 0, 64>(), 
               my_generator); 

    std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ", ")); 
    std::cout << std::endl; 
} 

それは

#include <utility> 
#include <array> 
#include <iostream> 
#include <algorithm> 
#include <iterator> 

/// the concept of a class that holds a range of something 
/// @requires T + 1 results in the next T 
/// @requires Begin + 1 + 1 + 1.... eventually results in Tn == End 
template<class T, T Begin, T End> 
struct range 
{ 
    constexpr T begin() const { return Begin; } 
    constexpr T end() const { return End; } 

    constexpr T size() const { return end() - begin(); } 
    using type = T; 
}; 

/// offset every integer in an integer sequence by a value 
/// e.g offset(2, <1, 2, 3>) -> <3, 4, 5> 
template<int Offset, int...Is> 
constexpr auto offset(std::integer_sequence<int, Is...>) 
{ 
    return std::integer_sequence<int, (Is + Offset)...>(); 
} 

/// generate a std::array by calling Gen(I) for every I in Is 
template<class T, class I, I...Is, class Gen> 
constexpr auto generate_array(std::integer_sequence<I, Is...>, Gen gen) 
{ 
    return std::array<T, sizeof...(Is)> { 
     gen(Is)... 
    }; 
} 

/// generate a std::array by calling Gen (x) for every x in Range 
template<class Range, class Gen> 
constexpr auto generate_array(Range range, Gen&& gen) 
{ 
    using T = decltype(gen(range.begin())); 
    auto from_zero = std::make_integer_sequence<typename Range::type, range.size()>(); 
    auto indexes = offset<range.begin()>(from_zero); 
    return generate_array<T>(indexes, std::forward<Gen>(gen)); 
} 

/// generate an integer by multiplying the input by 2 
constexpr int my_generator(int x) { 
    return 2 * x; 
} 

int main() 
{ 
    static constexpr auto arr = generate_array(range<int, 0, 64>(), 
               my_generator); 

    std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ", ")); 
    std::cout << std::endl; 
} 

を機能できるようにするために定型がここにいます組み立て前に見たコードが膨らんでいる:

.LC0: 
    .string ", " 
main: 
;; this is the start of the code that deals with the array 
    pushq %rbx 
    movl main::arr, %ebx 
.L2: 
    movl (%rbx), %esi 
    movl std::cout, %edi 
    addq $4, %rbx 
;; this is the end of it 

;; all the rest of this stuff is to do with streaming values to cout 
    call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 
    movl $2, %edx 
    movl $.LC0, %esi 
    movl std::cout, %edi 
    call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) 
    cmpq main::arr+256, %rbx 
    jne  .L2 
    movl std::cout, %edi 
    call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) 
    xorl %eax, %eax 
    popq %rbx 
    ret 
    subq $8, %rsp 
    movl std::__ioinit, %edi 
    call std::ios_base::Init::Init() 
    movl $__dso_handle, %edx 
    movl std::__ioinit, %esi 
    movl std::ios_base::Init::~Init(), %edi 
    addq $8, %rsp 
    jmp  __cxa_atexit 

main::arr: 
    .long 0 
    .long 2 
    .long 4 
    .long 6 
    .long 8 
    .long 10 
    .long 12 
    .long 14 
    .long 16 
    .long 18 
    .long 20 
    .long 22 
    .long 24 
    .long 26 
    .long 28 
    .long 30 
    .long 32 
    .long 34 
    .long 36 
    .long 38 
    .long 40 
    .long 42 
    .long 44 
    .long 46 
    .long 48 
    .long 50 
    .long 52 
    .long 54 
    .long 56 
    .long 58 
    .long 60 
    .long 62 
    .long 64 
    .long 66 
    .long 68 
    .long 70 
    .long 72 
    .long 74 
    .long 76 
    .long 78 
    .long 80 
    .long 82 
    .long 84 
    .long 86 
    .long 88 
    .long 90 
    .long 92 
    .long 94 
    .long 96 
    .long 98 
    .long 100 
    .long 102 
    .long 104 
    .long 106 
    .long 108 
    .long 110 
    .long 112 
    .long 114 
    .long 116 
    .long 118 
    .long 120 
    .long 122 
    .long 124 
    .long 126 

つまり、何もありません。

関連する問題