为什么不能建立参考向量?


351

当我这样做时:

std::vector<int> hello;

一切正常。但是,当我将其设为参考向量时:

std::vector<int &> hello;

我收到可怕的错误,例如

错误C2528:“指针”:指向引用的指针不合法

我想将一堆对结构的引用放到一个向量中,这样我就不必插手指针了。为什么vector对此大发脾气?我唯一的选择是使用指针向量吗?


46
您可以使用std :: vector <reference_wrapper <int>> hello; 见informit.com/guides/content.aspx?g=cplusplus&seqNum=217
艾米特

2
@amit链接不再有效,这里
Martin

Answers:


339

容器的组件类型(例如矢量)必须是可分配的。引用是不可分配的(您只能在声明它们时将其初始化一次,并且以后不能使它们引用其他内容)。其他不可分配的类型也不允许作为容器的组件,例如vector<const int>,不允许。


1
您是说我不能有向量的向量吗?(我确定我已经做到了...)
詹姆斯·柯伦

8
是的,std :: vector <std :: vector <int>>是正确的,std :: vector是可分配的。
马丁·科特

17
确实,这是“实际”的原因。关于T *不可能为T的错误是U&,这只是违反了必须分配T的要求的副作用。如果vector能够精确地检查类型参数,那么它可能会说“违反要求:T&不可分配”
Johannes Schaub-litb

2
boost.org/doc/libs/1_39_0/doc/html/Assignable.html上检查可分配的概念,除交换之外的所有操作都对引用有效。
艾米特

7
这不再是事实。从C ++ 11开始,对element的唯一与操作无关的要求是“可擦除的”,而引用不是。参见stackoverflow.com/questions/33144419/…
laike9m

119

是的,您可以寻找std::reference_wrapper模仿参考但可分配的并且也可以“重新安置”


4
get()在尝试访问此包装器中的类的实例的方法时,有没有一种方法可以先调用?例如reference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();不是很参考。
timdiels 2014年

3
它不是通过返回引用隐式地转换为Type本身吗?
WorldSEnder 2014年

3
是的,但这需要一个上下文,该上下文暗示需要进行转换。成员访问不这样做,因此需要.get()。什么timdiels想要的是operator.; 查看有关此问题的最新建议/讨论。
underscore_d

31

就其本质而言,只能在创建引用时设置引用。即,以下两行具有非常不同的效果:

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.

但是,创建矢量时,无法在创建时为其分配值。本质上,您只是在编写最后一个示例而已。


10
“创建矢量时,在创建时无法将值分配给它的项目”我不明白您所说的意思。什么是“创作时的物品”?我可以创建一个空向量。我可以使用.push_back()添加项目。您只是在指出引用不是默认可构造的。但是我绝对可以拥有不可默认构造的类的向量。
newacct

2
std :: vector <T>的元素ype不需要默认可构造。您可以编写struct A {A(int); 私人的:A(); }; vector <A> a; 很好-只要您不使用要求其默认可构造的方法(例如v.resize(100);-但您将需要执行v.resize(100,A(1));)
约翰尼斯·绍布

在这种情况下,您将如何编写这样的push_back()?它将仍然使用分配,而不是构造。
詹姆斯·柯伦

3
詹姆斯·柯伦(James Curran),那里没有默认构造。push_back只是将新闻A放入预分配的缓冲区中。看到这里:stackoverflow.com/questions/672352/…。请注意,我的主张仅是vector可以处理非默认可构造类型。我当然不声称它可以处理T&(当然不能)。
Johannes Schaub-litb

29

离子托迪雷尔已经提到了使用YES的答案std::reference_wrapper从C ++ 11开始,我们提供了一种机制,可以使用从中检索对象std::vector 并删除引用std::remove_reference。以下是使用g++and clangwith选项编译
-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";
    }             
}

12
我看不到std::remove_reference<>这里的价值。的要点std::remove_reference<>是允许您编写“类型T,但如果是类型T,则不作参考”。所以std::remove_reference<MyClass&>::type是一样的写作MyClass
alastair

2
这根本没有任何价值-您可以只写for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (或者,for (const MyClass& obj3: vec)如果您声明getval()const,则应如此)。
Toby Speight

14

boost::ptr_vector<int> 将工作。

编辑:建议使用std::vector< boost::ref<int> >,因为您无法默认构造a,因此该建议不起作用boost::ref


8
但是您可以使用向量或非默认可构造类型,对吗?您只需要注意不要使用默认的ctor。的向量
Manuel 2010年

1
@Manuel:或者resize
Lightness Races Orbit

5
注意,Boost的指针容器拥有指针的专有所有权。Quote:“当您确实需要共享语义时,就不需要此库了。”
马特乌斯布兰德

12

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


我猜可以使用void *缓冲区和新位置来实现。但这不是很有意义。
彼得

55
“语言缺陷”太强了。这是设计使然。我不认为vector必须与指向元素的指针一起使用。但是,要求该元素是可分配的。参考不是。
布赖恩·尼尔

您也不能sizeof参考。
大卫·史瓦兹

1
您不需要指向引用的指针,便拥有了引用,如果您确实需要指针,则只需保留指针本身即可;这里没有要解决的问题
Ion Todirel '17

10

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

长答案

作为标准建议,对于X包含类型对象的标准容器TT必须Erasable来自X

Erasable 表示以下表达式格式正确:

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

A是容器的分配器类型,m是分配器实例,并且p是type的指针*T。见这里Erasable定义。

默认情况下,std::allocator<T>用作向量的分配器。对于默认分配器,要求等同于的有效性p->~T()(请注意,T它是引用类型,并且p是指向引用的指针)。但是,指向引用的指针是非法的,因此该表达式的格式不正确。


3

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

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


4
这个答案不可行,因为ptr_vector应该是一个存储。那就是它将删除指针删除。因此,这对于他的目的不可用。
2014年

0

正如其他注释所建议的那样,您仅限于使用指针。但是,如果有帮助,这是一种避免直接面对指针的技术。

您可以执行以下操作:

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

reinterpret_cast不需要
Xeverous

1
如其他答案所示,我们完全不局限于使用指针。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.