web-dev-qa-db-ja.com

c配列のように 'const std :: vector <T>'を初期化する方法

const std::vector<const T>のようなconst T a[] = { ... }を作成し、固定(および少数)の値に初期化するエレガントな方法はありますか?
vector<T>を期待する関数を頻繁に呼び出す必要がありますが、私の場合、これらの値は変更されません。

原則として、私は

namespace {
  const std::vector<const T> v(??);
}

vはこのコンパイル単位の外部では使用されないためです。

78
vscharf

そのためには、C++ 0xを待つか、 Boost.Assign のようなものを使用する必要があります。

例えば。:

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;

c ++ 11の場合:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };
60
Ferruccio

Constベクターを初期化して興味深いコンテンツを作成する方法を尋ねる場合、おそらくその答えはコピーコンストラクターを使用することです。最初に面倒にベクトルを入力し、それから新しいconstベクトルを作成します。または、vector <InputIterator>(InputIterator、InputIterator)コンストラクターテンプレートを使用して、他の種類のコンテナーまたは配列から初期化できます。配列の場合、初期化リストを使用して定義できます。

このようなものがあなたの望むものに近いことを願っています:

const T ra[3] = {t1, t2, t3};
const vector<const T> v(ra, ra+3);

Constベクトルをベクトルを取る関数に渡す方法を尋ねる場合、答えは次のいずれかです。

  • 関数はベクトルを変更する可能性があり、オブジェクト/参照はconstであるため、できません。オリジナルの非constコピーを作成して渡します。

または

  • const_castを使用して、constnessを削除して、constを非constベクトルを受け取る関数に渡しますが、たまたまそのベクトルを変更しないことがわかっています。

後者は、それを見ると誰もがゴーグルについてコメントをすること、そして彼らが何もしないという事実を引き起こすことのできることの1つです。 const_castの目的はまさにそれですが、const_castが必要な場合はすでに失っているというかなり強力な議論があります。

これらの両方を実行する(コピーコンストラクターを使用して非constからconstベクターを作成し、constnessをキャストする)ことは間違いです。非constベクターを使用する必要があります。そのため、これらのうち最大1つを選択してください...

[編集:vector <T>とconst vector <const T>の違いについて話していることに気付いたところです。残念ながら、STLでは、vector <const T>とvector <T>は完全に無関係な型であり、それらを変換する唯一の方法はコピーすることです。これはベクトルと配列の違いです-T **は暗黙のうちに安全にconst T * const *に変換できます。

39
Steve Jessop

短くて汚い方法(Boostのlist_of()と同様)


#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(const T& t) {
        (*this)(t);
    }
    vlist_of& operator()(const T& t) {
        this->Push_back(t);
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
}

現在、C++ 11には初期化リストがあるため、そのようにする必要も、Boostを使用する必要もありません。しかし、例として、次のようにC++ 11で上記をより効率的に行うことができます。

#include <iostream>
#include <vector>
#include <utility>
#include <ostream>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(T&& t) {
        (*this)(move(t));
    }
    vlist_of& operator()(T&& t) {
        this->Push_back(move(t));
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    for (const auto& i: v) {
        cout << i << endl;
    }
}

しかし、vectorにはoperator =(vlist_of &&)が定義されていないため、C++ 11初期化リストを使用するほど効率的ではありません。

次のように修正されたtjohns20の方法は、より良いc ++ 11 vlist_ofである可能性があります。

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

template <typename T>
class vlist_of {
    public:
        vlist_of(T&& r) {
            (*this)(move(r));
        }
        vlist_of& operator()(T&& r) {
            v.Push_back(move(r));
            return *this;
        }
        vector<T>&& operator()() {
            return move(v);
        }
    private:
        vector<T> v;

};

int main() {
    const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
    for (const auto& i : v) {
        cout << i << endl;
    }

}
15
Shadow2531

他の人が言ったように、ソース配列へのポインタを与えない限り、Cスタイルの配列を初期化できるのと同じ方法でベクトルを初期化することはできません。しかし、その場合、ベクトルがグローバルconstである場合、代わりに古いCスタイルの配列を使用するだけではどうですか?

const int MyInts[] = {
1, 2, 3, 4, 5};

const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);

Constベクトルに対してアルゴリズムを使用するのと同じ方法で、この配列に対してSTLアルゴリズムを使用することもできます...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
12
John Dibling

次の2つの手順で実行できます。

namespace {
    const T s_actual_array[] = { ... };
    const std::vector<const T> s_blah(s_actual_array,
        s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
}

おそらくあなたが好きかもしれないほど美しくはありませんが、機能的です。

6
janm

どうですか:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
5
opal

古い質問ですが、今日同じ問題にぶつかりました。私の目的に最も適したアプローチを次に示します。

vector<int> initVector(void)
{
    vector<int> initializer;
    initializer.Push_back(10);
    initializer.Push_back(13);
    initializer.Push_back(3);
    return intializer;
}

int main()
{
    const vector<int> a = initVector();
    return 0;
}

過剰なコピーを避けるための例:

vector<int> & initVector(void)
{
    static vector<int> initializer;
    if(initializer.empty())
    {
        initializer.Push_back(10);
        initializer.Push_back(13);
        initializer.Push_back(3);
    }
    return intializer;
}

int main()
{
    const vector<int> & a = initVector();
    return 0;
}
3
Kevin

私があなたを正しく理解したかどうかはわかりません。私はあなたの質問を次のように理解しています:あなたはベクトルを多数の要素に初期化したいのです。ベクターでPush_back()を使用することの何が問題になっていますか? :-)

格納する要素の数がわかっている場合(または次の2のべき乗よりも少ない要素を格納することが確実な場合)、タイプXのポインターのベクトルがある場合、これを行うことができます(ポインターのみで機能します):

std::vector< X* > v;
v.reserve(num_elems);
X* p = v.begin();
for (int count = 0; count < num_elems; count++)
   p[count] = some_source[count];

Push_back()を使用している場合でも、次の2のべき乗以上の要素を追加することに注意してください。 v.begin()へのポインターは無効になります。

0
mstrobl

それらがすべて同じなら、あなたはちょうどできる

vector<T> vec(num_items, item);

しかし、私はそれらがそうではないと仮定します-その場合、最も近い方法はおそらく:

vector<T> vec(num_items);
vec[0] = 15;
vec[1] = 5;
...

C++ 0xを使用すると、イニシャライザリストを思い通りに使用できますが、残念ながら今のところあまり良くありません。

0
Peter

Shadow2531の応答に基づいて、シャドウのソリューションのようにstd :: vectorを実際に継承することなく、このクラスを使用してベクトルを初期化します。

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.Push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.Push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

使用法:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

Steve Jessopのソリューションと比較すると、より多くのコードが作成されますが、配列の作成がパフォーマンスに重要でない場合は、1行で配列を初期化する良い方法であることがわかります

0
tjohns20