मैं संदर्भों का वेक्टर क्यों नहीं बना सकता?


351

जब मैं ऐसा करता हूं:

std::vector<int> hello;

सब कुछ बढ़िया काम करता है। हालाँकि, जब मैं इसे संदर्भों का एक सदिश बनाता हूँ:

std::vector<int &> hello;

मुझे भयानक त्रुटियाँ मिलती हैं

त्रुटि C2528: 'पॉइंटर': पॉइंटर टू रेफरेंस अवैध है

मैं एक वेक्टर में स्ट्रक्चर्स के संदर्भ का एक गुच्छा रखना चाहता हूं, ताकि मुझे पॉइंटर्स के साथ ध्यान न देना पड़े। वेक्टर इस बारे में एक टेंट्रम क्यों फेंक रहा है? क्या मेरा एकमात्र विकल्प बजाय संकेत के वेक्टर का उपयोग करना है?


46
आप std :: वेक्टर <reference_wrapper <int>> हैलो का उपयोग कर सकते हैं; Informit.com/guides/content.aspx?g=cplusplus&seqNum=217
amit

2
@amit लिंक अब वैध नहीं है, आधिकारिक दस्तावेज यहां
मार्टिन

जवाबों:


339

वैक्टर जैसे कंटेनर के घटक प्रकार असाइन करने योग्य होने चाहिए । संदर्भ असाइन करने योग्य नहीं हैं (आप केवल एक बार उन्हें घोषित किए जाने पर उन्हें इनिशियलाइज़ कर सकते हैं, और आप उन्हें बाद में कुछ और संदर्भ नहीं दे सकते)। अन्य गैर-असाइन किए गए प्रकारों को भी कंटेनरों के घटकों के रूप में अनुमति नहीं दी जाती है, उदाहरण के vector<const int>लिए अनुमति नहीं है।


1
क्या आप कह रहे हैं कि मेरे पास वैक्टर का वेक्टर नहीं है? (मुझे यकीन है कि मैंने ऐसा किया है ...)
जेम्स कर्रान

8
हां, एक std :: वेक्टर <std :: वेक्टर <int>> सही है, std :: वेक्टर असाइन है।
मार्टिन कोटे

17
वास्तव में, यह "वास्तविक" कारण है। T * के बारे में त्रुटि T का असंभव होना U & है, केवल उल्लंघन की आवश्यकता का एक साइड-इफेक्ट है कि T को असाइन किया जाना चाहिए। यदि वेक्टर ठीक प्रकार पैरामीटर की जांच करने में सक्षम था, तो यह शायद कह सकते हैं कि "उल्लंघन की आवश्यकता: टी एंड नहीं आबंटित"
Johannes Schaub - litb

2
Boost.org/doc/libs/1_39_0/doc/html/Assignable.html पर असाइन किए गए कॉन्सेप्ट को चेक करते हुए स्वैप के अलावा सभी ऑपरेशन संदर्भों पर मान्य हैं।
अमित

7
यह अब सच नहीं है। C ++ 11 के बाद से, तत्व के लिए एकमात्र ऑपरेशन-स्वतंत्र आवश्यकता "इरेज़ेबल" होनी चाहिए, और संदर्भ नहीं है। Stackoverflow.com/questions/33144419/… देखें ।
laike9m

119

हाँ, आप देख सकते हैं, std::reference_wrapperकि एक संदर्भ की नकल करता है, लेकिन असाइन करने योग्य है और इसे "रीसेट" भी किया जा सकता है


4
क्या get()इस रैपर में क्लास के इंस्टेंस के किसी तरीके को एक्सेस करने की कोशिश करने पर कॉल करने का कोई तरीका है? जैसे reference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();बहुत संदर्भ नहीं है।
समयदिवस

3
क्या यह संदर्भ को वापस करके टाइप करने के लिए निहित नहीं है?
वर्ल्डसेंदर

3
हां, लेकिन इसके लिए एक संदर्भ की आवश्यकता होती है, जिसका अर्थ है कि रूपांतरण की आवश्यकता है। सदस्य पहुंच ऐसा नहीं करता है, इसलिए इसकी आवश्यकता है .get()। क्या timdiels चाहता है operator.; उस बारे में नवीनतम प्रस्तावों / चर्चाओं पर एक नज़र है।
अंडरस्कोर_ड

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 () के साथ आइटम जोड़ सकता हूं। आप केवल यह इंगित कर रहे हैं कि संदर्भ डिफ़ॉल्ट-निर्माण योग्य नहीं हैं। लेकिन मैं निश्चित रूप से उन वर्गों के वैक्टर हो सकता हूं जो डिफ़ॉल्ट-निर्माण योग्य नहीं हैं।
नवसंवत्सर

3
Std के तत्व ype :: वेक्टर <T> को डिफ़ॉल्ट रूप से रचनात्मक होने की आवश्यकता नहीं है। आप संरचना ए {ए (इंट) लिख सकते हैं; निजी: ए (); }; वेक्टर <A> ए; बस ठीक है - जब तक आप ऐसे तरीकों का उपयोग नहीं करते हैं जिनके लिए डिफ़ॉल्ट रूप से निर्माण करने की आवश्यकता होती है (जैसे v.resize (100); - लेकिन इसके बजाय आपको v.resize (100, A (1)) करने की आवश्यकता होगी;)
जोहान्स शाउब -

और आप इस मामले में ऐसा पुश_बैक () कैसे लिखेंगे? यह अभी भी असाइनमेंट का उपयोग करेगा, निर्माण का नहीं।
जेम्स कुर्रान

4
जेम्स क्यूरन, कोई डिफ़ॉल्ट निर्माण नहीं होता है। push_back प्रचारित बफर में सिर्फ प्लेसमेंट-समाचार A। यहाँ देखें: stackoverflow.com/questions/672352/... । ध्यान दें कि मेरा दावा केवल यह है कि वेक्टर गैर-डिफ़ॉल्ट-रचनात्मक प्रकारों को संभाल सकता है। मैं निश्चित रूप से दावा नहीं करता कि यह टीएंडडी को संभाल सकता है (यह निश्चित रूप से नहीं कर सकता है)।
जोहान्स स्काउब -

29

आयन टोडिरेल ने पहले ही एक जवाब का उल्लेख किया था जिसका उपयोग कर हाँ किया गया था std::reference_wrapperC ++ 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<>यह है कि आपको "टाइप टी लिखने की अनुमति है, लेकिन अगर यह एक है तो संदर्भ के बिना"। तो std::remove_reference<MyClass&>::typeबस लिखने जैसा ही है MyClass
अलस्टेयर

2
उस पर कोई मूल्य नहीं - आप बस लिख सकते हैं for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (या for (const MyClass& obj3: vec)यदि आप getval()कास्ट की घोषणा करते हैं , जैसा कि आपको करना चाहिए)।
टोबी स्पाइट

14

boost::ptr_vector<int> काम करेगा।

संपादित करें: उपयोग करने के लिए एक सुझाव था std::vector< boost::ref<int> >, जो काम नहीं करेगा क्योंकि आप डिफ़ॉल्ट रूप से निर्माण नहीं कर सकते हैं boost::ref


8
लेकिन आपके पास एक वेक्टर या गैर डिफ़ॉल्ट-रचनात्मक प्रकार हो सकते हैं, है ना? आपको केवल इस बात का ध्यान रखना है कि डिफ़ॉल्ट ctor का उपयोग न करें। वेक्टर का
मैनुअल

1
@ मैनुअल: या resize
ऑर्बिट

5
सावधान रहें, बूस्ट के पॉइंटर कंटेनर पॉइंटर का विशेष स्वामित्व लेते हैं। उद्धरण : "जब आपको साझा शब्दार्थ की आवश्यकता होती है, तो यह पुस्तकालय वह नहीं है जिसकी आपको आवश्यकता है।"
मैथ्यू ब्रैंडल

12

यह C ++ भाषा का दोष है। आप किसी संदर्भ का पता नहीं लगा सकते, क्योंकि ऐसा करने का प्रयास उस वस्तु के पते के परिणामस्वरूप किया जाएगा, और इस तरह आप कभी भी एक संदर्भ के लिए पॉइंटर प्राप्त नहीं कर सकते। std::vectorअपने तत्वों को संकेत के साथ काम करता है, इसलिए संग्रहीत किए जा रहे मूल्यों को इंगित करने में सक्षम होने की आवश्यकता होती है। आपको इसके बजाय पॉइंटर्स का उपयोग करना होगा।


मुझे लगता है कि इसे एक शून्य * बफर और प्लेसमेंट नए का उपयोग करके लागू किया जा सकता है। ऐसा नहीं है कि यह बहुत समझ में आता है।
पेट्रीचेन

55
"भाषा में दोष" बहुत मजबूत है। यह डिजाइन द्वारा है। मुझे नहीं लगता कि यह आवश्यक है कि वेक्टर तत्वों की ओर संकेत करता है। हालांकि यह आवश्यक है कि तत्व असाइन करने योग्य हो। सन्दर्भ नहीं हैं।
ब्रायन नील २ Brian

आप sizeofसंदर्भ भी नहीं ले सकते ।
डेविड श्वार्ट्ज

1
आपको संदर्भ के लिए एक संकेतक की आवश्यकता नहीं है, आपके पास संदर्भ है, यदि आपको सूचक की आवश्यकता है, तो बस सूचक को रखें; यहाँ हल करने के लिए कोई समस्या नहीं है
आयन टोडरेल

10

टी एल; डॉ

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होना चाहिए Erasableसे X

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 को भंडारण माना जाता है। यह है कि हटाने पर संकेत हटा देगा। इसलिए यह उसके उद्देश्य के लिए उपयोग करने योग्य नहीं है।
क्लेमेंस मॉर्गनस्टर्न

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
जैसा कि अन्य उत्तर बताते हैं, हम संकेत का उपयोग करने तक ही सीमित नहीं हैं।
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.