Почему я не могу сделать вектор ссылок? - PullRequest
303 голосов
/ 28 мая 2009

Когда я делаю это:

std::vector<int> hello;

Все отлично работает. Однако, когда я делаю это вектор ссылок вместо:

std::vector<int &> hello;

Я получаю ужасные ошибки, такие как

ошибка C2528: указатель: указатель на ссылку недопустим

Я хочу поместить группу ссылок на структуры в вектор, чтобы мне не приходилось вмешиваться в указатели. Почему вектор вызывает истерику по этому поводу? Мой единственный вариант использовать вместо него вектор указателей?

Ответы [ 9 ]

296 голосов
/ 28 мая 2009

Тип компонента контейнеров, таких как векторы, должен быть присваиваемый . Ссылки не могут быть назначены (вы можете инициализировать их только один раз, когда они объявлены, и вы не можете заставить их ссылаться на что-то другое позже). Другие не присваиваемые типы также не допускаются в качестве компонентов контейнеров, например vector<const int> не допускается.

104 голосов
/ 20 ноября 2012

да, вы можете найти std::reference_wrapper, который имитирует ссылку, но может быть назначен, а также может быть "переустановлен"

27 голосов
/ 28 мая 2009

По своей природе ссылки могут быть установлены только во время их создания; то есть следующие две строки имеют очень разные эффекты:

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.

Однако, когда вы создаете вектор, нет способа присвоить значения его элементам при создании. По сути, вы просто делаете целую кучу из последнего примера.

25 голосов
/ 29 мая 2015

Ион Тодирель уже упомянул ответ ДА с использованием std::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";
    }             
}
14 голосов
/ 28 мая 2009

boost::ptr_vector<int> будет работать.

Редактировать: было предложено использовать std::vector< boost::ref<int> >, которое не будет работать, потому что вы не можете создать по умолчанию boost::ref.

12 голосов
/ 28 мая 2009

Это недостаток языка C ++. Вы не можете получить адрес ссылки, так как попытка сделать это приведет к адресу объекта, на который делается ссылка, и, таким образом, вы никогда не сможете получить указатель на ссылку. std::vector работает с указателями на его элементы, поэтому на хранимые значения необходимо указывать. Вместо этого вам придется использовать указатели.

3 голосов
/ 28 мая 2009

Как уже упоминалось, вы, вероятно, в итоге будете использовать вместо него вектор указателей.

Однако вы можете рассмотреть возможность использования ptr_vector вместо!

1 голос
/ 22 марта 2019

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

Демо

Длинный ответ

Как предполагает стандарт , для стандартного контейнера X, содержащего объекты типа T, T должен быть Erasable из X.

Erasable означает, что следующее выражение правильно сформировано:

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

A - тип распределителя контейнера, m - экземпляр распределителя, а p - указатель типа *T. См. здесь для определения Erasable.

По умолчанию std::allocator<T> используется в качестве распределителя вектора. При использовании распределителя по умолчанию требование эквивалентно допустимости p->~T() (обратите внимание, что T является ссылочным типом, а p является указателем на ссылку). Однако указатель на ссылку недопустим , поэтому выражение сформировано некорректно.

0 голосов
/ 24 мая 2017

Как показывают другие комментарии, вы ограничены использованием указателей. Но если это поможет, вот одна из техник, позволяющая избежать прямого обращения с указателями.

Вы можете сделать что-то вроде следующего:

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