【问题标题】:Why can't I make a vector of references?为什么我不能制作参考向量?
【发布时间】:2021-09-15 21:02:50
【问题描述】:

当我这样做时:

std::vector<int> hello;

一切都很好。但是,当我将其设为引用向量时:

std::vector<int &> hello;

我遇到了像

这样的可怕错误

error C2528: 'pointer' : 指向引用的指针是非法的

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

【问题讨论】:

标签: c++ vector reference stl container-data-type


【解决方案1】:

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

【讨论】:

  • 我猜它可以使用 void * 缓冲区和放置 new 来实现。并不是说这很有意义。
  • “语言缺陷”太强了。这是设计使然。我认为向量不需要与指向元素的指针一起使用。但是,要求元素是可分配的。参考不是。
  • 您也不能将sizeof 作为参考。
  • 你不需要指向引用的指针,你有引用,如果你确实需要指针,只需保留指针本身;这里没有问题需要解决
【解决方案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.

但是,当您创建向量时,无法在创建时为其项分配值。您实际上只是在制作最后一个示例。

【讨论】:

  • “当你创建一个向量时,没有办法在创建时为它的项目分配值”我不明白你的意思是什么。什么是“它的创造物”?我可以创建一个空向量。我可以使用 .push_back() 添加项目。您只是指出引用不是默认可构造的。但我绝对可以拥有不可默认构造的类向量。
  • std::vector 的元素类型不需要是默认可构造的。你可以写 struct A { A(int);私人:A(); };向量 a;很好 - 只要你不使用要求它是默认可构造的方法(比如 v.resize(100); - 但是你需要做 v.resize(100, A(1)); )
  • 在这种情况下你会如何编写这样的 push_back() 呢?它仍然会使用赋值,而不是构造。
  • James Curran,这里没有默认构造。 push_back 只是将placement-news A 放入预分配的缓冲区。见这里:stackoverflow.com/questions/672352/…。请注意,my 声明只是该向量可以处理非默认可构造类型。当然,我并没有声称它可以处理 T&(当然不能)。
【解决方案3】:

vector 等容器的组件类型必须为assignable。引用是不可分配的(您只能在声明它们时初始化它们一次,并且以后不能让它们引用其他东西)。其他不可分配的类型也不允许作为容器的组件,例如不允许使用vector&lt;const int&gt;

【讨论】:

  • 你是说我不能有向量的向量吗? (我确定我已经做到了……)
  • 是的,一个 std::vector > 是正确的,std::vector 是可赋值的。
  • 确实,这是“实际”原因。关于 T* 不可能 T is U& 的错误只是违反了 T 必须是可分配的要求的副作用。如果vector能够精确检查类型参数,那么它可能会说“违反要求:T&不可分配”
  • 检查 boost.org/doc/libs/1_39_0/doc/html/Assignable.html 的可分配概念除交换之外的所有操作在引用上都有效。
  • 这不再是真的。从 C++11 开始,对元素的唯一独立于操作的要求是“可擦除”,而引用则不是。见stackoverflow.com/questions/33144419/…
【解决方案4】:

boost::ptr_vector&lt;int&gt; 可以工作。

编辑:建议使用std::vector&lt; boost::ref&lt;int&gt; &gt;,这将不起作用,因为您不能默认构造boost::ref

【讨论】:

  • 但是你可以有一个向量或不可默认构造的类型,对吧?您只需要注意不要使用默认 ctor。向量的
  • @Manuel:或者resize
  • 小心,Boost 的指针容器拥有指针的独占所有权。 Quote:“当你确实需要共享语义时,这个库不是你所需要的。”
【解决方案5】:

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

不过,您可能需要考虑改用ptr_vector

【讨论】:

  • 这个答案不可行,因为 ptr_vector 应该是一个存储。也就是说,它将在删除时删除指针。所以它不能用于他的目的。
  • 您必须以一种或另一种方式将某些东西存储在某个地方!文档:“std::reference_wrapper 的实例是对象(它们可以被复制或存储在容器中)”
【解决方案6】:

是的,你可以,寻找 std::reference_wrapper,它模仿引用但可分配,也可以“重新安装”

【讨论】:

  • 在尝试访问此包装器中的类实例的方法时,是否有办法绕过首先调用get()?例如。 reference_wrapper&lt;MyClass&gt; my_ref(...); my_ref.get().doStuff(); 不是很像。
  • 不是通过返回引用隐式转换为 Type 本身吗?
  • 是的,但这需要一个暗示需要进行哪种转换的上下文。会员访问不这样做,因此需要.get()timdiels 想要的是operator.;查看有关此的最新提案/讨论。
  • 但是请注意,虽然 reference_wrapper 不像原始引用类型可以“重新安装”,但它仍然不能未初始化,因此它不是默认可构造的。这意味着虽然可以构造vector&lt;reference_wrapper&lt;int&gt;&gt;{int1, int2, int3},但仍然不能拥有带有默认构造元素的向量:vector&lt;reference_wrapper&lt;int&gt;&gt;(5)。这甚至没有被 IDE (CLion) 捕获,但编译失败。
  • 我觉得这在&lt;functional&gt; 中声明很奇怪——它似乎比仅仅可调用对象更通用。
【解决方案7】:

Ion Todirel 已经使用std::reference_wrapper 提到了一个答案YES从 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";
    }             
}

【讨论】:

  • 我在这里看不到 std::remove_reference&lt;&gt; 的值。 std::remove_reference&lt;&gt; 的要点是允许您编写“类型 T,但如果它是一个则不是引用”。所以std::remove_reference&lt;MyClass&amp;&gt;::type 和写MyClass 是一样的。
  • 没有任何价值 - 你可以写 for (MyClass obj3 : vec) std::cout &lt;&lt; obj3.getval() &lt;&lt; "\n"; (或者 for (const MyClass&amp; obj3: vec) 如果你声明 getval() const,你应该这样做)。
【解决方案8】:

正如其他 cmets 所建议的,您只能使用指针。 但如果有帮助,这里有一种技术可以避免直接面对指针。

您可以执行以下操作:

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 不需要
  • 正如其他答案所示,我们根本不局限于使用指针。
【解决方案9】:

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

长答案

作为standard suggests,对于包含T 类型对象的标准容器XT 必须是来自XErasable

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

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

A 是容器的分配器类型,m 是分配器实例,p*T 类型的指针。有关Erasable 的定义,请参见here

默认情况下,std::allocator&lt;T&gt; 用作向量的分配器。使用默认分配器,要求等同于p-&gt;~T() 的有效性(注意T 是引用类型,p 是指向引用的指针)。但是,pointer to a reference is illegal,因此表达式格式不正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多