वेक्टर के वेक्टर में अपरिभाषित व्यवहार


19

यह कोड प्रतीत होता है कि अनधिकृत पूर्णांक की अपरिभाषित संख्या क्यों लिखता है?

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    for (int i : vector<vector<int>>{{77, 777, 7777}}[0])
        cout << i << ' ';
}

मुझे उम्मीद थी कि आउटपुट होगा 77 777 7777

क्या यह कोड अपरिभाषित होना चाहिए?

जवाबों:


18

vector<vector<int>>{{77, 777, 7777}}एक अस्थायी है और फिर रंग vector<vector<int>>{{77, 777, 7777}}[0]-बिरंगे में इस्तेमाल करना अपरिभाषित व्यवहार होगा।

आपको पहले एक वैरिएबल बनाना चाहिए, जैसे

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    auto v = vector<vector<int>>{{77, 777, 7777}};
    for(int i: v[0])
        cout << i << ' ';
}

इसके अलावा यदि आप Clang 10.0.0 का उपयोग करते हैं तो यह इस व्यवहार के बारे में चेतावनी देता है।

चेतावनी: सूचक का समर्थन करने वाली वस्तु पूर्ण-अभिव्यक्ति के अंत में नष्ट हो जाएगी [-Wdangling-gsl] वेक्टर> {{77, 777, 7777}} [0]


2
इस बुरे अभ्यास को फैलने से रोकने के लिए कृपया using std::vectorइसके बजाय उपयोग करें using namespace std;
अनंत

10

ऐसा इसलिए है क्योंकि जिस वेक्टर को आप देख रहे हैं वह लूप में प्रवेश करने से पहले नष्ट हो जाएगा।

यह आमतौर पर होता है:

auto&& range = vector<vector<int>>{{77, 777, 7777}}[0];
auto&& first = std::begin(range);
auto&& last = std::end(range);
for(; first != last; ++first)
{
    int i = *first;
    // the rest of the loop
}

समस्याएं पहली पंक्ति में शुरू होती हैं क्योंकि इसका मूल्यांकन इस प्रकार है:

  1. सबसे पहले दिए गए तर्कों के साथ वैक्टर के वेक्टर का निर्माण करें और यह वेक्टर अस्थायी हो जाता है क्योंकि इसका कोई नाम नहीं है।

  2. फिर श्रेणी संदर्भ उप-वेक्टर के लिए बाध्य है जो केवल उसी वेक्टर के रूप में मान्य होगा जब तक कि इसमें शामिल वेक्टर मान्य हो।

  3. एक बार अर्धविराम तक पहुँचने के बाद अस्थायी वेक्टर नष्ट हो जाता है और इसके विध्वंसक में यह नष्ट हो जाता है और किसी भी संग्रहीत वैक्टर को नष्ट कर देता है जिसमें सबस्क्रिप्टेड शामिल होता है।

  4. आप एक नष्ट हो चुके वेक्टर के संदर्भ के साथ समाप्त होते हैं, जिसे खत्म किया जाएगा।

इस समस्या से बचने के लिए दो उपाय हैं:

  1. लूप से पहले वेक्टर को घोषित करें ताकि यह तब तक चले जब तक इसका दायरा समाप्त न हो जाए जिसमें लूप शामिल हो।

  2. C ++ 20 एक init स्टेटमेंट के साथ आता है जो इन समस्याओं को हल करने के लिए प्रदान किया गया है और पहले दृष्टिकोण से बेहतर है यदि आप चाहते हैं कि वेक्टर लूप के तुरंत बाद नष्ट हो जाए:

    for (vector<vector<int>> vec{{77, 777, 7777}}; int i : vec[0])
    {
    }

यह वह नहीं है जो "आम तौर पर" होता है। यह सटीक व्यवहार (नामकरण के बारे में उचित स्कूपिंग और विचार) मानक के अनुसार अनिवार्य है, जैसा कि अगर नियम है।
कोनराड रुडोल्फ

मैं जीवनकाल का जिक्र कर रहा हूं। यहां तक ​​कि अगर आप एक ही कोड हाथ से लिखते हैं, तो आपके पास केवल गारंटी है कि आपको वांछित व्यवहार मिलता है और कंपाइलर वह करेगा जो वह अनुकूलन के साथ कर सकता है
dev65

टीआईएल सी ++ 20 सिंटेक्स के लिए घोषित रेंज-के लिए। यह सुनिश्चित नहीं है कि खुश या दुखी होना चाहिए।
विंग्स विथ विथ

6
vector<vector<int>>{{77, 777, 7777}}[0]

मुझे उम्मीद है कि यह झूल रहा है।

हालांकि एक राउंडेड फॉर की परिभाषा यह आश्वस्त करती है कि बृहदान्त्र की आरएचएस अवधि के लिए "जीवित" बनी हुई है, आप अभी भी एक अस्थायी सदस्यता ले रहे हैं। केवल सबस्क्रिप्ट के परिणाम को रखा गया है, लेकिन यह परिणाम एक संदर्भ है, और वास्तविक वेक्टर पूर्ण-अभिव्यक्ति से अतीत में नहीं बच सकता है जिसमें यह घोषित किया गया है। जिसमें पूरे लूप का वर्णन नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.