ChatGPT解决这个技术问题 Extra ChatGPT

为什么我不能制作参考向量?

当我这样做时:

std::vector<int> hello;

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

std::vector<int &> hello;

我得到可怕的错误,比如

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

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

你可以使用 std::vector<reference_wrapper<int> >你好;请参阅informit.com/guides/content.aspx?g=cplusplus&seqNum=217
@amit 链接不再有效,官方文档here

l
laike9m

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


是的, std::vector< 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/…
@johnbakers:您正在修改引用指向的东西,而不是引用本身。修改引用本身将允许您将引用指向其他东西,这对于 C++ 引用是不可能的。
u
user1438233

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


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

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

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& (当然不能)。
M
Milan

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<> 中的值。 std::remove_reference<> 的要点是允许您编写“类型 T,但如果它是一个则不是引用”。所以 std::remove_reference<MyClass&>::type 与写 MyClass 是一样的。
没有任何价值 - 你可以只写 for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (或者如果你声明 getval() const,你应该写 for (const MyClass& obj3: vec))。
i
ivaigult

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<T> 用作向量的分配器。使用默认分配器,要求等同于 p->~T() 的有效性(注意 T 是引用类型,p 是指向引用的指针)。但是,pointer to a reference is illegal,因此表达式的格式不正确。


A
Adam Rosenfield

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


我想它可以使用 void * 缓冲区和放置 new 来实现。并不是说这很有意义。
“语言缺陷”太强了。这是设计使然。我认为向量不需要与指向元素的指针一起使用。但是,要求元素是可分配的。参考文献不是。
您也不能将 sizeof 作为参考。
您不需要指向引用的指针,您有引用,如果确实需要指针,只需保留指针本身;这里没有问题需要解决
D
Drew Dormann

boost::ptr_vector<int> 将起作用。

编辑: 是使用 std::vector< boost::ref<int> > 的建议,因为您不能默认构造 boost::ref,所以它不起作用。


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

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

但是,您可能需要考虑改用 ptr_vector


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

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

[8.3.2-5.s1] 不应该有对引用的引用,没有引用数组,也没有指向引用的指针。


O
Omid

正如其他评论所暗示的那样,您仅限于使用指针。但如果它有帮助,这里有一种避免直接面对指针的技术。

您可以执行以下操作:

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 不需要
正如其他答案所示,我们根本不限于使用指针。