Std :: कतार :: पॉप रिटर्न मान क्यों नहीं है?


123

मैं इस पृष्ठ के माध्यम से गया था, लेकिन मैं उसी के कारण प्राप्त करने में सक्षम नहीं हूं। वहाँ यह उल्लेख है कि

"यह अधिक समझदार है कि इसके लिए कोई मूल्य नहीं है और ग्राहकों को सामने वाले का उपयोग करने के लिए () कतार के सामने के मूल्य का निरीक्षण करने की आवश्यकता है"

लेकिन सामने () से एक तत्व का निरीक्षण करने के लिए भी उस तत्व की आवश्यकता होती है जो कि प्रतिरूप में कॉपी किया जाता है। उदाहरण के लिए इस कोड सेगमेंट में

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.push (myint);

/ * यहां अस्थायी RHS पर बनाया जाएगा जिसे परिणाम के लिए सौंपा जाएगा, और यदि संदर्भ द्वारा रिटर्न मिलता है तो परिणाम पॉप ऑपरेशन के बाद अमान्य हो जाएगा * /

result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

पांचवीं पंक्ति पर cout ऑब्जेक्ट पहले myqueue.front () की एक प्रति बनाता है फिर परिणाम देने के लिए असाइन करता है। तो, अंतर क्या है, पॉप फ़ंक्शन एक ही काम कर सकता है।


क्योंकि इस तरह से लागू किया जाता है (यानी, void std::queue::pop();)।
101010

इस सवाल का जवाब दिया गया है, लेकिन एक सनद के रूप में: यदि आप वास्तव में एक पॉप चाहते हैं जो रिटर्न करता है, तो इसे आसानी से एक मुफ्त फ़ंक्शन के साथ लागू किया जा सकता है: ideone.com/lnUzf6
eerorika

1
आपका लिंक STL प्रलेखन के लिए है। लेकिन आप C ++ मानक पुस्तकालय के बारे में पूछ रहे हैं। अलग अलग बातें।
जुआनकोपनज़ा

5
"लेकिन एक तत्व का निरीक्षण front()करने के लिए भी उस तत्व की आवश्यकता होती है जो कि लवल्यू में कॉपी किया जाता है" - नहीं। frontएक संदर्भ देता है, एक मूल्य नहीं। आप इसे कॉपी किए बिना संदर्भित मूल्य का निरीक्षण कर सकते हैं।
माइक सीमोर

1
@KeminZhou आप जिस मॉडल का वर्णन करते हैं, उसे एक प्रतिलिपि की आवश्यकता होती है। शायद। यदि आप कतार का मल्टीप्लेक्स उपभोग करना चाहते हैं, तो हाँ, आपको कतार पर ताला जारी करने से पहले एक प्रतिलिपि बनाना होगा। हालांकि, यदि आप केवल इनपुट और आउटपुट को अलग करने के बारे में परवाह करते हैं, तो आपको सामने के निरीक्षण के लिए लॉक की आवश्यकता नहीं है । आप तब तक लॉक करने के लिए इंतजार कर सकते हैं जब तक आप इसका उपभोग नहीं करते हैं और कॉल करने की आवश्यकता होती है pop()। यदि आप उपयोग करते हैं std::queue<T, std::list<T>>तो front()ए द्वारा अमान्य होने से प्रदान किए गए संदर्भ के बारे में कोई चिंता नहीं है push()। लेकिन आपको अपने उपयोग के पैटर्न को जानना चाहिए और अपनी बाधाओं का दस्तावेजीकरण करना चाहिए।
jwm

जवाबों:


105

तो, अंतर क्या है, पॉप फ़ंक्शन एक ही काम कर सकता है।

यह वास्तव में एक ही काम कर सकता था। इसका कारण यह नहीं था, क्योंकि पॉप पॉप वाला तत्व लौटाया जाने वाला अपवाद अपवादों की उपस्थिति में असुरक्षित है (मूल्य से वापस लौटना और इस प्रकार प्रतिलिपि बनाना)।

इस परिदृश्य पर विचार करें (एक भोले / बने पॉप कार्यान्वयन के साथ, मेरी बात को आगे बढ़ाने के लिए):

template<class T>
class queue {
    T* elements;
    std::size_t top_position;
    // stuff here
    T pop()
    {
        auto x = elements[top_position];
        // TODO: call destructor for elements[top_position] here
        --top_position;  // alter queue state here
        return x;        // calls T(const T&) which may throw
    }

यदि टी का कॉपी कंस्ट्रक्टर रिटर्न पर फेंकता है, तो आप पहले ही कतार की स्थिति ( top_positionमेरे भोले कार्यान्वयन में) बदल चुके हैं और तत्व को कतार से हटा दिया जाता है (और वापस नहीं)। सभी इरादों और उद्देश्यों के लिए (कोई फर्क नहीं पड़ता कि आप क्लाइंट कोड में अपवाद को कैसे पकड़ते हैं) कतार के शीर्ष पर स्थित तत्व खो जाता है।

यह कार्यान्वयन उस मामले में भी अक्षम है जब आपको पॉपप किए गए मूल्य की आवश्यकता नहीं होती है (अर्थात यह उस तत्व की एक प्रति बनाता है जिसका कोई उपयोग नहीं करेगा)।

यह दो अलग-अलग संचालन ( void popऔर const T& front()) के साथ सुरक्षित और कुशलता से लागू किया जा सकता है ।


हम्म् ... जो समझ में आता है
cbinder

37
C ++ 11 नोट: यदि T के पास एक सस्ता नॉइसेसेप्ट मूव-कंस्ट्रक्टर है (जो अक्सर स्टैक में रखी गई वस्तुओं के प्रकार के लिए होता है) तो वैल्यू द्वारा लौटना कुशल और अपवाद-सुरक्षित है।
रोमन एल

9
@ DavidRodríguez-dribeas: मैं किसी भी बदलाव का सुझाव नहीं दे रहा हूं, मेरी बात यह थी कि कुछ कमियां C ++ 11 के साथ किसी समस्या से कम नहीं हैं।
रोमन एल

11
लेकिन इसे क्यों कहा जाता है pop? यह काफी प्रति-सहज है। इसे नाम दिया जा सकता है drop, और फिर यह सभी के लिए स्पष्ट है कि यह तत्व को पॉप नहीं करता है, लेकिन इसे इसके बजाय छोड़ देता है ...
UeliDeSchwert

12
@utnapistim: डे-फैक्टो "पॉप" ऑपरेशन हमेशा एक स्टैक से शीर्ष तत्व को लेने और उसे वापस करने के लिए किया गया है। जब मैं पहली बार एसटीएल स्टैक में आया तो मुझे आश्चर्य हुआ, कम से कम कहने के लिए, popकुछ भी वापस नहीं करने के बारे में । उदाहरण के लिए विकिपीडिया पर देखें ।
संकट लुएंगो

34

आपके प्रश्न का उत्तर देने के लिए आपने जो पेज लिंक किया है।

पूरे खंड को प्रासंगिक बनाने के लिए:

किसी को आश्चर्य हो सकता है कि value_type के बजाय pop () क्यों void देता है। यही कारण है, एक एकल सदस्य फ़ंक्शन में दो के संयोजन के बजाय, क्यू के सामने तत्व को जांचने और हटाने के लिए किसी को फ्रंट () और पॉप () का उपयोग क्यों करना चाहिए? वास्तव में, इस डिजाइन का एक अच्छा कारण है। यदि पॉप () ने सामने वाले तत्व को वापस कर दिया, तो उसे संदर्भ के बजाय मान से वापस लौटना होगा: संदर्भ द्वारा वापसी एक झूलने वाला सूचक पैदा करेगा। मूल्य से वापसी, हालांकि, अक्षम है: इसमें कम से कम एक निरर्थक कॉपी कंस्ट्रक्टर कॉल शामिल है। चूँकि यह पॉप () के लिए इस तरह से एक मूल्य को वापस करना असंभव है, क्योंकि यह दोनों कुशल और सही है, इसलिए इसके लिए यह अधिक समझदार है कि किसी भी मूल्य पर वापस न आए और ग्राहकों को सामने वाले का उपयोग करने की आवश्यकता हो () मूल्य का निरीक्षण करने के लिए कतार के सामने।

C ++ को प्रोग्रामर द्वारा लिखी गई कोड की लाइनों की संख्या को ध्यान में रखते हुए दक्षता के साथ डिज़ाइन किया गया है।


16
शायद, लेकिन असली कारण यह है कि एक अपवाद के लिए एक सुरक्षित सुरक्षित संस्करण (मजबूत गारंटी के साथ) को लागू करना असंभव है, popजिसमें से एक मान लौटाता है।
जेम्स कांजे जूल

5

पॉप उस मान के संदर्भ को वापस नहीं कर सकता है जिसे हटा दिया गया है, क्योंकि यह डेटा संरचना से हटाया जा रहा है, इसलिए संदर्भ को क्या संदर्भित करना चाहिए? यह मूल्य से वापस आ सकता है, लेकिन क्या होगा अगर पॉप का परिणाम कहीं भी संग्रहीत नहीं है? फिर समय बेकार में मूल्य की नकल कर रहा है।


4
असली कारण अपवाद सुरक्षा है। स्टैक के साथ "लेन-देन" करने का कोई सुरक्षित तरीका नहीं है (यदि तत्व स्टैक पर रहता है, या यह आपके लिए वापस आ गया है) यदि popरिटर्न और वापसी का कार्य एक अपवाद हो सकता है। स्पष्ट रूप से इसे वापस करने से पहले तत्व को निकालना होगा, और फिर अगर कुछ फेंकता है तो तत्व अपरिवर्तनीय रूप से खो सकता है।
जॉन

1
क्या यह चिंता अभी भी एक noexcept चाल निर्माता के साथ लागू होती है? (बेशक 0 प्रतियां अधिक कुशल हैं कि दो चलती हैं, लेकिन यह एक अपवाद सुरक्षित, प्रभावी संयुक्त मोर्चा + पॉप के लिए दरवाजा खोल सकता है)
peppe

1
@peppe, यदि आप जानते हैं कि आप value_typeनोटह्रो मूव कंस्ट्रक्टर हैं, तो आप वैल्यू द्वारा लौट सकते हैं , लेकिन कतार इंटरफ़ेस तब किस प्रकार की वस्तु पर निर्भर करता है कि आप इसमें स्टोर करते हैं, जो मददगार नहीं होगा।
जोनाथन वेकली जूल

1
@peppe: यह सुरक्षित होगा, लेकिन स्टैक एक सामान्य लाइब्रेरी क्लास है और इसलिए जितना अधिक यह उपयोगी होता है तत्व के बारे में उतना ही कम होता है।
जॉन

मुझे पता है कि एसटीएल इसके लिए अनुमति नहीं देगा, लेकिन इसका मतलब यह है कि कोई इस सुविधा के साथ अपनी खुद की कतार वर्ग का निर्माण कर सकता है और ^ W ^ W ^ इस सुविधा के साथ :)
peppe

3

वर्तमान कार्यान्वयन के साथ, यह मान्य है:

int &result = myqueue.front();
std::cout << result;
myqueue.pop();

यदि पॉप एक संदर्भ देता है, तो इस तरह:

value_type& pop();

तब निम्न कोड क्रैश हो सकता है, क्योंकि संदर्भ अब मान्य नहीं है:

int &result = myqueue.pop();
std::cout << result;

दूसरी ओर, अगर यह सीधे मान लौटाएगा:

value_type pop();

फिर आपको इस कोड को काम करने के लिए एक प्रतिलिपि करने की आवश्यकता होगी, जो कम कुशल है:

int result = myqueue.pop();
std::cout << result;

1

C ++ 11 से शुरू करके चाल शब्दार्थों का उपयोग करके वांछित व्यवहार को संग्रहित करना संभव होगा। की तरह pop_and_move। इसलिए कॉपी कंस्ट्रक्टर को नहीं बुलाया जाएगा, और प्रदर्शन केवल मूव कंस्ट्रक्टर पर निर्भर करेगा।


2
नहीं, चाल शब्दार्थ जादुई रूप से popअपवाद को सुरक्षित नहीं बनाता है।
LF

0

आप पूरी तरह से ऐसा कर सकते हैं:

std::cout << ' ' << myqueue.front();

या, यदि आप किसी चर में मान चाहते हैं, तो संदर्भ का उपयोग करें:

const auto &result = myqueue.front();
if (result > whatever) do_whatever();
std::cout << ' ' << result;

इसके आगे: शब्द 'अधिक समझदार' का एक व्यक्तिपरक रूप है 'हमने उपयोग पैटर्न में देखा और विभाजन की अधिक आवश्यकता पाई।' (निश्चिंत रहें: C ++ भाषा हल्के से विकसित नहीं हो रही है ...)


0

मुझे लगता है कि सबसे अच्छा समाधान कुछ ऐसा जोड़ना होगा

std::queue::pop_and_store(value_type& value);

जहां मूल्य पॉपअप मूल्य प्राप्त होगा।

लाभ यह है कि यह एक चाल असाइनमेंट ऑपरेटर का उपयोग करके कार्यान्वित किया जा सकता है, जबकि फ्रंट + पॉप का उपयोग करके एक प्रतिलिपि बनाई जाएगी।

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