27

多くの言語には、コレクションの初期化に役立つジェネレーターがあります。C ++では、ベクトルを均一に初期化したい場合は、次のように記述できます。

std::vector<int> vec(10, 42); // get 10 elements, each equals 42

その場で異なる値を生成したい場合はどうなりますか?たとえば、10個のランダムな値、または0から9までの連続した数字で初期化しますか?この構文は便利ですが、C++11では機能しません。

int cnt = 0;
std::vector<int> vec(10, [&cnt]()->int { return cnt++;});

反復関数呼び出しによってコレクションを初期化する良い方法はありますか?私は現在、この醜いパターンを使用しています(ループよりもはるかに読みやすく/短い):

std::vector<int> vec;
int cnt = 0;
std::generate_n(std::back_inserter(vec), 10, [&cnt]()->int { return cnt++;});

役立つことがあり、それは最初のコンストラクターの欠如を説明するでしょう。関数と呼び出し回数を受け取るイテレーターを想像できるので、コンストラクターは

vector ( InputIterator first, InputIterator last);

該当します。しかし、私は標準ライブラリでこのようなものを見つけられませんでした。私はそれを逃しましたか?最初のコンストラクターが標準に到達しなかった別の理由はありますか?

4

5 に答える 5

15

残念ながら、これを行うための標準的な機能はありません。

counting_iterator特定の例では、Boost.Iterator を次のように使用できます。

std::vector<int> v(boost::counting_iterator<int>(0),
    boost::counting_iterator<int>(10));

または、Boost.Range を次のように使用することもできます。

auto v(boost::copy_range<std::vector<int>>(boost::irange(0,10)));

(copy_range基本的にreturn std::vector<int>(begin(range), end(range))は、2 つのイテレータによる範囲構築のみをサポートする既存のコンテナに全範囲構築を採用するための優れた方法です。)


std::randここで、ジェネレーター関数 ( など)を使用した汎用の場合には、 function_input_iterator. インクリメントされると、ジェネレーターを呼び出して結果を保存し、逆参照するときに返されます。

#include <vector>
#include <iostream>
#include <cmath>
#include <boost/iterator/function_input_iterator.hpp>

int main(){
  std::vector<int> v(boost::make_function_input_iterator(std::rand, 0),
      boost::make_function_input_iterator(std::rand,10));
  for(auto e : v)
    std::cout << e << " ";
}

実例。

悲しいことに、function_input_iteratorBoost.ResultOf を使用しないため、関数ポインタまたは入れ子になった を持つresult_type関数オブジェクト型が必要です。ラムダには、何らかの理由でそれがありません。それを定義する(または) オブジェクトにラムダを渡すことができます。を使用した例を次に示します。Boost.Iterator がいつか Boost.ResultOf を利用するようになることを期待するしかありません。std::functionboost::functionstd::functiondecltypeBOOST_RESULT_OF_USE_DECLTYPE

于 2012-09-20T11:53:00.247 に答える
6

世界は広すぎて、C++ ですべてのソリューションを出荷することはできません。ただし、C++ は、考えられるすべての味覚に対応する調理済み食品でいっぱいの巨大なスーパーマーケットにはなりたくありません。むしろ、それは設備の整った小さなキッチンであり、C++ のマスター シェフであるあなたが望むあらゆるソリューションを調理することができます。

以下は、シーケンス ジェネレーターのばかげた非常に粗雑な例です。

#include <iterator>

struct sequence_iterator : std::iterator<std::input_iterator_tag, int>
{
    sequence_iterator() : singular(true) { }
    sequence_iterator(int a, int b) : singular(false) start(a), end(b) { }
    bool singular;
    int start;
    int end;

    int operator*() { return start; }
    void operator++() { ++start; }

    bool operator==(sequence_iterator const & rhs) const
    {
        return (start == end) == rhs.singular;
    }
    bool operator!=(sequence_iterator const & rhs) const
    {
        return !operator==(rhs);
    }
};

今、あなたは言うことができます:

std::vector<int> v(sequence_iterator(1,10), sequence_iterator());

同様に、「特定のファンクターを特定の回数呼び出す」などのより一般的なガジェットを作成することもできます (たとえば、テンプレート化されたコピーによって関数オブジェクトを取得し、カウンターを繰り返しカウンターとして使用し、逆参照によってファンクターを呼び出します)。 )。

于 2012-09-20T11:52:10.497 に答える
2

質問で使用するラムダをサポートするコンパイラを使用している場合はstd::iota、それが含まれている可能性が非常に高く、少なくともカウントケースが少しきれいになります。

std::vector <int> vec(10);
std::iota(begin(vec), end(vec), 0);

このシナリオ(および他のかなりの数のシナリオ)では、私たちは本当に好むでしょうiota_n

namespace stdx {
template <class FwdIt, class T>
void iota_n(FwdIt b, size_t count, T val = T()) {
    for ( ; count; --count, ++b, ++val)
        *b = val;
}
}

これは、あなたの場合、次のように使用します。

std::vector<int> vec;

stdx::iota_n(std::back_inserter(vec), 10);

これが標準ライブラリに含まれていなかった理由については、私は本当に推測することさえできません。これは範囲を支持する引数と見なすことができると思うので、アルゴリズムは範囲を取り、開始/終了ペアまたは開始/カウントペアのいずれかから範囲を作成する簡単な方法があります。完全に同意するかどうかはわかりませんが、範囲はうまく機能する場合もありますが、ほとんどまたはまったく意味がない場合もあります。これ以上の作業がなければ、イテレータのペアよりもはるかに優れた答えがあるかどうかはわかりません。

于 2012-09-20T16:44:05.493 に答える
2

boost::assignについて誰も言及していないので、ここで紹介します。

#include <iostream>
#include <vector>
#include <boost/assign/std/vector.hpp> 
#include <cstdlib>

int main()
{
    std::vector<int> v1;
    std::vector<int> v2;
    boost::assign::push_back(v1).repeat_fun(9, &rand);
    int cnt = 0;
    boost::assign::push_back(v2).repeat_fun(10, [&cnt]()->int { return cnt++;});
    for (auto i : v1)
    {
        std::cout << i << ' ';
    }
    std::cout << std::endl;
    for (auto i : v2)
    {
        std::cout << i << ' ';
    }
}

出力

41 18467 6334 26500 19169 15724 11478 29358 26962
0 1 2 3 4 5 6 7 8 9

于 2012-09-20T21:41:02.953 に答える
0

SFINAEを使用してテーブルを作成できます。

#include <iostream>
#include <vector>

template <int n> struct coeff    { static int const value = coeff<n-1>::value + 3; };
template <>      struct coeff<0> { static int const value = 0; };

template<int... values> struct c1 {static int const value[sizeof...(values)];};
template<int... values> int const c1<values...>::value[] = {values...};

template<int n, int... values> struct c2 : c2< n-1, coeff<n-1>::value, values...> {};
template<int... values> struct c2< 0, values... > : c1<values...> {};

template<int n> struct table : c2< n > {
    static std::vector< unsigned int > FormTable()
    {
        return std::vector< unsigned int >( & c2< n >::value[0], & c2< n >::value[n] );
    }
};

int main()
{
    const auto myTable = table< 20 >::FormTable();

    for ( const auto & it : myTable )
    {
        std::cout<< it << std::endl;
    }
}
于 2012-09-20T11:48:20.573 に答える