当我这样做的时候:

std::vector<int> hello;

一切都很好。然而,当我把它变成一个引用向量时:

std::vector<int &> hello;

我就会犯可怕的错误

指向引用的指针是非法的

我想把一堆对struct的引用放到一个vector中,这样我就不用管指针了。为什么矢量会为此大发脾气?我唯一的选择是使用指针的向量代替?


这是c++语言的一个缺陷。您不能获取引用的地址,因为尝试这样做将导致被引用对象的地址,因此您永远无法获得指向引用的指针。vector与指向其元素的指针一起工作,因此存储的值需要能够被指向。你将不得不使用指针代替。


根据它们的本质,引用只能在创建时设置;也就是说,下面两行有非常不同的效果:

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.

然而,当你创建一个向量时,没有办法在创建时给它的项赋值。实际上你只是做了一大堆上一个例子。


容器的组件类型(如向量)必须是可赋值的。引用是不可赋值的(只能在声明时初始化它们一次,以后不能让它们引用其他东西)。其他不可赋值类型也不允许作为容器的组件,例如vector<const int>是不允许的。


Boost::ptr_vector<int>将工作。

Edit:是一个建议使用std::vector< boost::ref<int> >,这将不起作用,因为你不能默认构造boost::ref。


正如其他人所提到的,您可能最终会使用指针向量。

但是,您可能想要考虑使用ptr_vector代替!


是的,你可以,寻找std::reference_wrapper,它模仿引用,但是可赋值的,也可以“重新定位”


Ion Todirel已经使用std::reference_wrapper提到了一个答案是。从c++ 11开始,我们就有了从std::vector中检索object并使用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";
        }             
    }

正如其他注释所建议的那样,您只能使用指针。 但如果它有帮助,这里有一个技巧来避免直接面对指针。

你可以这样做:

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

TL;博士

像这样使用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

长回答

正如标准所建议的,对于包含类型T的对象的标准容器X, T必须是可从X擦除的。

Erasable表示下面的表达式形式良好:

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

A是容器的分配器类型,m是分配器实例,p是类型*T的指针。请参见这里的可擦定义。

默认情况下,std::allocator<T>被用作vector的分配器。对于默认的分配器,这个要求相当于p->~T()的有效性(注意T是一个引用类型,p是指向引用的指针)。但是,指向引用的指针是非法的,因此表达式不是格式良好的。


我在c++ 14中找到了原因,“国际标准ISO/IEC 14882:2014(E)编程语言c++”

(8.3.2-5.s1) 没有引用的引用,没有引用的数组,也没有引用的指针。


没有技术上的原因为什么你不能有一个引用向量,它只是不是API的一部分,大概是为了保持它类似于数组。可以很容易地添加专门化,使用引用,在内部将它们存储为指针,并在API中显示为引用:

vector<int&> ivref;
int i = 42;
ivref.push_back(i); // address of i is stored
ivref.front() = 43; // i is set to 43
ivref.push_back(44); // Marked deleted for rvalue references 
ivref.resize(10);    // Marked deleted for vector of references