เหตุใดฉันจึงไม่สามารถสร้างเวกเตอร์ของการอ้างอิงได้


351

เมื่อฉันทำสิ่งนี้:

std::vector<int> hello;

ทุกอย่างใช้งานได้ดี อย่างไรก็ตามเมื่อฉันทำให้เป็นเวกเตอร์ของการอ้างอิงแทน:

std::vector<int &> hello;

ฉันได้รับข้อผิดพลาดที่น่ากลัวเช่น

ข้อผิดพลาด C2528: 'ตัวชี้': ตัวชี้ไปยังการอ้างอิงนั้นผิดกฎหมาย

ฉันต้องการใส่การอ้างอิงจำนวนมากเพื่อวางโครงสร้างในเวกเตอร์เพื่อที่ฉันจะได้ไม่ต้องยุ่งกับพอยน์เตอร์ ทำไมเวกเตอร์ถึงโกรธเคืองเรื่องนี้? ตัวเลือกเดียวของฉันคือใช้เวกเตอร์พอยน์เตอร์แทนหรือไม่


46
คุณสามารถใช้ std :: vector <reference_wrapper <int>> hello; ดูinformit.com/guides/content.aspx?g=cplusplus&seqNum=217
amit

2
@ แก้ไขลิงก์ไม่ถูกต้องเอกสารทางการที่นี่
Martin

คำตอบ:


339

ประเภทส่วนประกอบของภาชนะบรรจุเช่นเวกเตอร์จะต้องมอบหมาย การอ้างอิงไม่สามารถกำหนดได้ (คุณสามารถเริ่มต้นได้เมื่อมีการประกาศและคุณไม่สามารถอ้างอิงได้ในภายหลัง) ประเภทที่ไม่สามารถกำหนดได้อื่น ๆ ก็ไม่ได้รับอนุญาตเช่นเดียวกับส่วนประกอบของตู้คอนเทนเนอร์เช่นvector<const int>ไม่ได้รับอนุญาต


1
คุณกำลังบอกว่าฉันไม่มีเวกเตอร์เวกเตอร์หรือไม่? (ฉันแน่ใจว่าฉันได้ทำที่ ... )
เจมส์เคอร์แร

8
ใช่ std :: vector <std :: vector <int>> ถูกต้อง std :: vector สามารถกำหนดได้
มาร์ติน Cote

17
อันที่จริงนี่คือเหตุผล "ที่เกิดขึ้นจริง" ข้อผิดพลาดเกี่ยวกับ T * ที่เป็นไปไม่ได้ของ T คือ U & เป็นเพียงผลข้างเคียงของข้อกำหนดที่ละเมิดที่ต้องกำหนด T หากเวกเตอร์สามารถตรวจสอบพารามิเตอร์ประเภทได้อย่างแม่นยำก็อาจจะพูดว่า "ข้อกำหนดที่ละเมิด: T & ไม่สามารถกำหนดได้"
Johannes Schaub - litb

2
การตรวจสอบแนวคิดที่กำหนดได้ที่boost.org/doc/libs/1_39_0/doc/html/Assignable.htmlการดำเนินการทั้งหมดยกเว้นการแลกเปลี่ยนจะมีผลกับการอ้างอิง
amit

7
สิ่งนี้ไม่เป็นความจริงอีกต่อไป ตั้งแต่ C ++ 11 ข้อกำหนดเฉพาะที่ไม่ขึ้นกับการดำเนินการสำหรับองค์ประกอบจะต้องเป็น "Erasable" และการอ้างอิงไม่ใช่ ดูstackoverflow.com/questions/33144419/...
laike9m

119

ใช่คุณสามารถค้นหาstd::reference_wrapperว่าเป็นการลอกเลียนแบบการอ้างอิง แต่สามารถกำหนดได้และยังสามารถ "ซ้ำ"


4
มีวิธีรับสายget()ก่อนเมื่อพยายามเข้าถึงวิธีการตัวอย่างของชั้นเรียนในเสื้อคลุมนี้หรือไม่? เช่นreference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();การอ้างอิงไม่มาก
timdiels

3
มันไม่สามารถใช้กับ Type เองได้โดยการส่งคืนข้อมูลอ้างอิงหรือไม่
WorldSEnder

3
ใช่ แต่ต้องมีบริบทที่แสดงถึงการแปลงที่จำเป็น .get()การเข้าถึงสมาชิกไม่ได้ทำจึงจำเป็นที่จะต้อง สิ่งที่Timdielsต้องการคือoperator.; ดูข้อเสนอ / การสนทนาล่าสุดเกี่ยวกับเรื่องนั้น
underscore_d

32

โดยธรรมชาติแล้วการอ้างอิงสามารถตั้งค่าได้ในเวลาที่สร้างขึ้นเท่านั้น คือสองบรรทัดต่อไปนี้มีเอฟเฟกต์ที่แตกต่างกันมาก:

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

3
องค์ประกอบ ype ของ std :: vector <T> ไม่จำเป็นต้องเป็นค่าเริ่มต้นที่สามารถสร้างได้ คุณสามารถเขียน struct A {A (int); ส่วนตัว: A (); }; เวกเตอร์ <A> a; ทำได้ดี - ตราบใดที่คุณไม่ใช้วิธีการดังกล่าวซึ่งต้องการให้มันเป็นค่าเริ่มต้นที่สามารถสร้างได้ (เช่น v.resize (100); - แต่คุณจะต้องทำ v.resize (100, A (1));)
Johannes Schaub - litb

และคุณจะเขียน push_back () ในกรณีนี้ได้อย่างไร? มันจะยังคงใช้การมอบหมายไม่ใช่การก่อสร้าง
James Curran

4
James Curran, ไม่มีการก่อสร้างเริ่มต้นเกิดขึ้นที่นั่น push_back เป็นเพียงตำแหน่งข่าวไปสู่บัฟเฟอร์ที่จัดสรรล่วงหน้า ดูที่นี่: stackoverflow.com/questions/672352/… . โปรดทราบว่าการอ้างสิทธิ์ของฉันเป็นเพียงเวกเตอร์ที่สามารถจัดการประเภทที่ไม่ใช่ค่าเริ่มต้นที่สร้างได้ ฉันไม่ได้อ้างสิทธิ์แน่นอนว่ามันสามารถจัดการ T & (มันไม่แน่นอน)
Johannes Schaub - litb

29

ไอออน Todirel กล่าวถึงคำตอบที่ใช่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";
    }             
}

12
ฉันไม่เห็นคุณค่าในstd::remove_reference<>ที่นี่ จุดประสงค์std::remove_reference<>คือเพื่อให้คุณสามารถเขียน "ประเภท 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> >boost::ref


8
แต่คุณสามารถมีเวกเตอร์หรือชนิดที่ไม่ใช่ค่าเริ่มต้นที่สร้างได้ใช่ไหม คุณจะต้องระมัดระวังไม่ใช้ ctor เริ่มต้น ของเวกเตอร์
Manuel


5
ระวังตัวชี้ของ Boost จะเป็นเจ้าของกรรมสิทธิ์ของผู้รับ อ้างอิง : "เมื่อคุณต้องการซีแมนทิกส์ที่ใช้ร่วมกันไลบรารีนี้ไม่ใช่สิ่งที่คุณต้องการ"
Matthäus Brandl

12

มันเป็นข้อบกพร่องในภาษา C ++ คุณไม่สามารถใช้ที่อยู่ของการอ้างอิงได้เนื่องจากพยายามทำเช่นนั้นจะส่งผลให้ที่อยู่ของวัตถุนั้นถูกอ้างถึงและทำให้คุณไม่สามารถรับตัวชี้ไปยังการอ้างอิงได้ std::vectorทำงานร่วมกับพอยน์เตอร์ขององค์ประกอบดังนั้นค่าที่ถูกจัดเก็บจะต้องสามารถชี้ไปได้ คุณจะต้องใช้พอยน์เตอร์แทน


ฉันเดาว่ามันสามารถนำไปใช้งานได้โดยใช้ void * buffer และการวางตำแหน่งใหม่ ไม่ว่านี่จะสมเหตุสมผลมาก
peterchen

55
"ข้อบกพร่องในภาษา" แข็งแกร่งเกินไป มันเป็นโดยการออกแบบ ฉันไม่คิดว่ามันเป็นสิ่งจำเป็นที่เวกเตอร์ทำงานกับพอยน์เตอร์ไปยังองค์ประกอบ อย่างไรก็ตามจำเป็นต้องมีองค์ประกอบที่สามารถกำหนดได้ การอ้างอิงไม่ใช่
Brian Neal

คุณไม่สามารถใช้การsizeofอ้างอิงได้เช่นกัน
David Schwartz

1
คุณไม่ต้องการตัวชี้ไปยังการอ้างอิงคุณมีการอ้างอิงถ้าคุณต้องการตัวชี้เพียงแค่เก็บตัวชี้ไว้ ไม่มีปัญหาที่จะแก้ไขได้ที่นี่
Ion Todirel

10

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

Demo

คำตอบที่ยาว

ในฐานะที่เป็นมาตรฐานให้เห็นสำหรับภาชนะมาตรฐานXที่มีวัตถุประเภทT, Tต้องมีจากErasableX

Erasable หมายความว่าการแสดงออกต่อไปนี้จะเกิดขึ้นอย่างดี:

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

Aเป็นประเภทจัดสรรคอนเทนเนอร์, mอินสแตนซ์จัดสรรและเป็นตัวชี้ประเภทp *Tดูที่นี่สำหรับErasableความหมาย

โดยค่าเริ่มต้นstd::allocator<T>จะใช้เป็นตัวจัดสรรของเวกเตอร์ ด้วยตัวจัดสรรเริ่มต้นข้อกำหนดจะเทียบเท่ากับความถูกต้องของp->~T()(หมายเหตุTคือประเภทการอ้างอิงและpเป็นตัวชี้ไปยังการอ้างอิง) อย่างไรก็ตามตัวชี้ไปยังการอ้างอิงนั้นผิดกฎหมายดังนั้นการแสดงออกจึงไม่เกิดขึ้น


3

ดังที่คนอื่น ๆ พูดถึงคุณอาจจะจบลงด้วยการใช้เวกเตอร์พอยน์เตอร์แทน

อย่างไรก็ตามคุณอาจต้องการใช้ptr_vectorแทน!


4
คำตอบนี้ไม่สามารถใช้งานได้เนื่องจาก ptr_vector ควรจะเป็นที่เก็บข้อมูล นั่นคือมันจะลบตัวชี้เมื่อทำการลบ ดังนั้นจึงไม่สามารถใช้งานได้สำหรับจุดประสงค์ของเขา
Klemens Morgenstern

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.