सबसे नीचे संपादित करें


मुझे pair के कंस्ट्रक्टर के साथ एक व्यवहार मिला, जिसे मैं पूरी तरह से नहीं समझता।

तो मैं इस कोड के साथ, रावल्यू के साथ एक जोड़ी शुरू करने का प्रयास करता हूं:

pair<vector<int> &&, int> a([]() -> vector<int> {
    vector<int> b{1};
    cout << &b << ' ' << b[0] << '\n';
    return b;
}(), 0);

cout << &a.first << ' ' << a.first[0] << '\n';

आउटपुट है

0x62fdf0 1
0x62fdf0 14162480

तो जाहिरा तौर पर a.first कचरा है।

तब मुझे pair का कंस्ट्रक्टर ऑनलाइन मिलता है, ऐसा होने के लिए:

pair (const first_type& a, const second_type& b);
template<class U, class V> pair (U&& a, V&& b);

तो मुझे लगता है कि दूसरा इस्तेमाल किया जा रहा है? फिर मैंने && को हटाने का प्रयास किया:

pair<vector<int>, int> a([]() -> vector<int> {
    vector<int> b{1};
    cout << &b << ' ' << b[0] << '\n';
    return b;
}(), 0);

cout << &a.first << ' ' << a.first[0] << '\n';

लेकिन अब a.first का पता b से अलग है:

0x62fdf0 1
0x62fdc0 1

लेकिन अगर मैं बाहरी बेकार pair को हटा देता हूं, तो कोड काम करेगा (यानी एक ही पता और एक ही मूल्य)। क्यों? और मैं pair कैसे काम कर सकता हूं?


संपादित करें

@cigien द्वारा टिप्पणियों को समझने की कोशिश करने के बाद, मैंने पुराने कोड को बहुत कम कर दिया

pair<int &&, int> a(1, 2);
cout << a.first << '\n';

जिसने मुझे त्रुटि warning: '<anonymous>' is used uninitialized in this function [-Wuninitialized] के साथ प्रेरित किया। फिर मैंने इस पोस्ट को उसी चेतावनी के बारे में पढ़ा

मैंने pair कंस्ट्रक्टर यहां। जो नीचे (अधिक वेनिला) कोड की ओर जाता है:

struct s_t {
    int && first = 0;
} var;

int main() {
    var.first = 2;
    cout << var.first << '\n';
    cout << var.first << '\n';
}

इस कोड से चेतावनी चली गई है। लेकिन इस कोड का आउटपुट है:

2
0

जिसने ईमानदारी से मुझे शून्य सुराग छोड़ दिया। किसी भी मदद की सराहना की जाती है।

1
user161070 13 सितंबर 2020, 19:33

1 उत्तर

सबसे बढ़िया उत्तर

आप अस्थायी वस्तुओं बना रहे हैं, उनके लिए संदर्भ बना रहे हैं, और फिर अंतर्निहित वस्तु के चले जाने के बाद उन संदर्भों का उपयोग करने का प्रयास कर रहे हैं।

पहले उदाहरण में, []() -> vector<int> { ... }() एक प्रचलन (वस्तु बनाने के निर्देश) प्रकार vector<int> की अभिव्यक्ति है। pair कंस्ट्रक्टर को वास्तविक ऑब्जेक्ट के संदर्भ की आवश्यकता होती है, ऑब्जेक्ट बनाने के लिए निर्देश नहीं, इसलिए पहले कंस्ट्रक्टर को बुलाया जाता है, लैम्ब्डा को कॉल किया जाता है और एक अस्थायी vector<int> बनाया जाता है . फिर कंस्ट्रक्टर उस ऑब्जेक्ट के संदर्भ को pair में स्टोर करता है, और फिर कंस्ट्रक्टर के समाप्त होने के बाद आप उस ऑब्जेक्ट को नष्ट कर देते हैं और नियंत्रण आपके पास लौट आता है। इसलिए जोड़ी का पहला तत्व अब कचरा है।

दूसरे में, कंस्ट्रक्टर अस्थायी vector का संदर्भ प्राप्त करता है (और अस्थायी उसी तरह से बनाया जाता है), लेकिन इस बार, संदर्भ को संग्रहीत करने के बजाय (यानी vector&& वेक्टर&& से, यह संदर्भ से एक वेक्टर बनाता है। वेक्टर को वेक्टर&& से बनाना वेक्टर के कंस्ट्रक्टर के एक निश्चित अधिभार द्वारा नियंत्रित किया जाता है (यह अधिभार का विशेष नाम "मूव कंस्ट्रक्टर") है। यह कंस्ट्रक्टर अस्थायी ऑब्जेक्ट के अंदर हीप एलोकेशन के लिए हैंडल लेता है और बस जोड़ी के अंदर ऑब्जेक्ट को ओनरशिप देता है। अब, एक बार अस्थायी नष्ट हो जाने पर, वास्तविक डेटा बच जाता है, लेकिन हैंडल (यानी vector ऑब्जेक्ट) स्वयं एक अलग जगह पर होता है।

आपके अंतिम उदाहरण में पहले जैसी ही समस्या है। जब var को इनिशियलाइज़ किया जाता है, 0 (एक int प्रचलन) एक वास्तविक वस्तु को संदर्भित नहीं करता है, इसलिए आप इसके लिए केवल एक संदर्भ var.first को बाध्य नहीं कर सकते। 0 को एक अस्थायी वस्तु में बदल दिया जाता है, var.first को उस वस्तु को संदर्भित करने के लिए बनाया जाता है, और फिर अस्थायी को नष्ट कर दिया जाता है। var.first अब लटक रहा है और आप इसके साथ कुछ नहीं कर सकते हैं, इसलिए शेष कोड यूबी है और यह पता लगाने का कोई मतलब नहीं है कि "हुड के नीचे" क्या गलत हो रहा है (हालांकि यह निश्चित रूप से दिलचस्प लगता है)। वास्तव में, किसी संदर्भ सदस्य को इस तरह से अस्थायी रूप से प्रारंभ करना इतना बेकार है कि दोष रिपोर्ट 1696 के अनुसार var का प्रारंभ अवैध है ("कठिन" संकलन त्रुटि होनी चाहिए) (लेकिन पुराने संकलक इसे स्वीकार कर सकते हैं, या यहां तक ​​कि वर्तमान वाले भी (गलत तरीके से) इसे चेतावनी के रूप में अवनत कर सकते हैं।)

अब, यदि आप वास्तव में vector "सीधे" को जोड़ी के अंदर इसके मूव कंस्ट्रक्टर को कॉल किए बिना बनाना चाहते हैं, तो आप ऐसा कुछ कर सकते हैं जहां आप एक बनाते हैं ऑब्जेक्ट जो वेक्टर के निर्माण को उस समय तक निलंबित कर देता है जब तक कि जोड़ी के क्षेत्र का निर्माण नहीं हो जाता।

template<typename F>
struct initializing {
    F self;
    initializing(F self) : self(std::move(self)) { }
    operator decltype(auto)() { return self(); }
};

और अब कहो

pair<vector<int>, int> a(
    initializing([]() -> vector<int> {
        vector<int> b{1};
        cout << &b << ' ' << b[0] << '\n';
        return b;
    }), 0);

अब, लैम्ब्डा एक अनाम क्लोजर प्रकार का प्रचलन है। यह एक अस्थायी वस्तु में बदल जाता है, और एक प्रारंभिक प्रचलन उस अस्थायी के संदर्भ को प्रारंभिक के कंस्ट्रक्टर के पास भेजकर उत्पन्न किया जाता है। अब, उस प्रचलन को भी एक अस्थायी रूप में बदल दिया गया है, जो कंस्ट्रक्टर को कॉल करता है और क्लोजर टाइप के मूव कंस्ट्रक्टर (इस मामले में एक नहीं -op) अस्थायी क्लोजर टाइप ऑब्जेक्ट के संदर्भ में। फिर उस अस्थायी प्रारंभिक ऑब्जेक्ट का संदर्भ जोड़ी के कंस्ट्रक्टर को दिया जाता है, जो से वेक्टर का निर्माण करता है। प्रारंभ करना. यह प्रारंभिक में परिभाषित रूपांतरण फ़ंक्शन को परिणाम ऑब्जेक्ट के साथ जोड़ी के अप्रारंभीकृत फ़ील्ड पर सेट करता है। रूपांतरण फ़ंक्शन तब लैम्ब्डा को कॉल करता है जिसके परिणाम ऑब्जेक्ट को जोड़ी के क्षेत्र में भी सेट किया जाता है। लैम्ब्डा फिर उस परिणाम वस्तु (जोड़ी का पहला तत्व) को सीधे b को रिटर्न पर ले जाकर प्रारंभ करता है (या, जब NRVO लागू किया जाता है, b परिणाम वस्तु/जोड़ी के पहले तत्व के लिए एक नाम बन जाता है और लैम्ब्डा के शीर्ष पर आरंभ किया जाता है)। इस तरह, लैम्ब्डा में लिखे एक या दो "स्पष्ट रूप से" और b और a.first के पतों को छोड़कर किसी भी वेक्टर कंस्ट्रक्टर को कॉल नहीं किया जाता है। कोड> वही होगा यदि एनआरवीओ लागू किया जाता है। थोड़ा और खतरनाक संस्करण है

template<typename F>
struct initializing {
    F &&self;
    initializing(F &&self) : self(std::forward<F>(self)) { }
    operator decltype(auto)() { return std::forward<F>(self)(); }
};

जो लैम्ब्डा से भौतिक वस्तु की गति को भी समाप्त करता है।

0
HTNW 16 सितंबर 2020, 19:59