web-dev-qa-db-ja.com

参照のベクトルを作成できないのはなぜですか?

私がこれを行うとき:

std::vector<int> hello;

すべてがうまく機能します。ただし、代わりに参照のベクトルにする場合:

std::vector<int &> hello;

次のような恐ろしいエラーが発生します

エラーC2528: 'pointer':参照へのポインターが無効です

構造体への参照の束をベクトルに入れたいので、ポインターをいじる必要はありません。ベクトルがこれについてかんしゃくを投げるのはなぜですか?代わりにポインターのベクトルを使用する唯一のオプションはありますか?

303
Colen

ベクトルなどのコンテナのコンポーネントタイプは assignable でなければなりません。参照は割り当てられません(宣言されたときに初期化できるのは一度だけで、後で他の何かを参照させることはできません)。他の割り当て不可能なタイプも、コンテナのコンポーネントとして許可されていません。 vector<const int>は許可されていません。

296
newacct

はい、できます std::reference_wrapper を探します

103
Ion Todirel

本質的に、参照は作成時にのみ設定できます。つまり、次の2行の効果は大きく異なります。

int & A = B;   // makes A an alias for B
A = C;         // assigns value of C to B.

さらに、これは違法です:

int & D;       // must be set to a int variable.

ただし、ベクトルを作成する場合、作成時にそのアイテムに値を割り当てる方法はありません。あなたは本質的に最後の例の束を作っているだけです。

27
James Curran

イオン・トディレルはすでに答えを述べていましたYESstd::reference_wrapperを使用しています。 C++ 11からstd::vectorからオブジェクトを取得し、std::remove_referenceを使用して参照を削除するメカニズムがあります。以下に、g++およびclangをオプション付きで使用してコンパイルした例を示します
-std=c++11および正常に実行されました。

#include <iostream>
#include <vector>
#include<functional>

class MyClass {
public:
    void func() {
        std::cout << "I am func \n";
    }

    MyClass(int y) : x(y) {}

    int getval()
    {
        return x;
    }

private: 
        int x;
};

int main() {
    std::vector<std::reference_wrapper<MyClass>> vec;

    MyClass obj1(2);
    MyClass obj2(3);

    MyClass& obj_ref1 = std::ref(obj1);
    MyClass& obj_ref2 = obj2;

    vec.Push_back(obj_ref1);
    vec.Push_back(obj_ref2);

    for (auto obj3 : vec)
    {
        std::remove_reference<MyClass&>::type(obj3).func();      
        std::cout << std::remove_reference<MyClass&>::type(obj3).getval() << "\n";
    }             
}
25
Steephen

boost::ptr_vector<int> は機能します。

Edit:std::vector< boost::ref<int> >を使用することを提案しました。これはboost::refをデフォルトで構築できないため機能しません。

14
Drew Dormann

これは、C++言語の欠陥です。参照しようとすると、オブジェクトのアドレスが参照されるため、参照のアドレスを取得できません。したがって、参照へのポインタを取得することはできません。 std::vectorは、その要素へのポインターで動作するため、格納されている値を指すことができる必要があります。代わりにポインターを使用する必要があります。

12
Adam Rosenfield

他の人が言及したように、おそらくポインターのベクトルを代わりに使用することになります。

ただし、代わりに ptr_vector を使用することを検討してください。

3
Martin Cote

TL; DR

std::reference_wrapper を次のように使用します。

#include <functional>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    std::string hello = "Hello, ";
    std::string world = "everyone!";
    typedef std::vector<std::reference_wrapper<std::string>> vec_t;
    vec_t vec = {hello, world};
    vec[1].get() = "world!";
    std::cout << hello << world << std::endl;
    return 0;
}

Demo

長い答え

標準の提案 のように、タイプXのオブジェクトを含む標準コンテナTの場合、TErasableXでなければなりません。

Erasableは、次の式の形式が正しいことを意味します。

allocator_traits<A>::destroy(m, p)

Aはコンテナのアロケータタイプ、mはアロケータインスタンス、pはタイプ*Tのポインタです。 Erasableの定義については、 here を参照してください。

デフォルトでは、std::allocator<T>がベクターのアロケーターとして使用されます。デフォルトのアロケーターでは、要件はp->~T()の有効性と同等です(Tは参照型であり、pは参照へのポインターです)。ただし、- 参照へのポインターは無効です であるため、式の形式が適切ではありません。

1
ivaigult

他のコメントが示唆しているように、あなたはポインタの使用に限定されています。しかし、それが役立つ場合、ポインターに直接直面することを避けるための1つのテクニックがあります。

次のようなことができます。

vector<int*> iarray;
int default_item = 0; // for handling out-of-range exception

int& get_item_as_ref(unsigned int idx) {
   // handling out-of-range exception
   if(idx >= iarray.size()) 
      return default_item;
   return reinterpret_cast<int&>(*iarray[idx]);
}
0
Omid