संरचना और std :: pair का उपयोग करने में क्या अंतर है?


26

मैं सीमित अनुभव के साथ C ++ प्रोग्रामर हूं।

यह मानकर कि मैं STL mapकुछ डेटा को स्टोर और मैनिपुलेट करने के लिए उपयोग करना चाहता हूं , मैं जानना चाहूंगा कि क्या उन 2 डेटा संरचना दृष्टिकोणों के बीच कोई सार्थक अंतर (प्रदर्शन में भी) है:

Choice 1:
    map<int, pair<string, bool> >

Choice 2:
    struct Ente {
        string name;
        bool flag;
    }
    map<int, Ente>

विशेष रूप से, वहाँ structएक सरल के बजाय किसी भी उपरि है pair?


18
एक std::pair है एक struct।
कैलेथ

3
@gnat: इस तरह के सामान्य प्रश्न इस तरह के विशिष्ट प्रश्नों के लिए उपयुक्त उपयुक्त लक्ष्य होते हैं, खासकर यदि विशिष्ट उत्तर डुबकी लक्ष्य (जो इस मामले में संभावना नहीं है) पर मौजूद नहीं है।
रॉबर्ट हार्वे

18
@ कैलेथ - std::pairएक टेम्पलेट हैstd::pair<string, bool>एक संरचना है।
पीट बेकर

4
pairपूरी तरह से शब्दार्थ से रहित है। कोई भी आपके कोड को नहीं पढ़ रहा है (भविष्य में आपके सहित) को पता चल जाएगा कि e.firstकिसी चीज़ का नाम तब तक है जब तक आप स्पष्ट रूप से उसे इंगित नहीं करते। मुझे लगता है कि में एक फर्म आस्तिक हूँ pairकरने के लिए एक बहुत ही गरीब और आलसी अलावा था std, और जब यह कल्पना की गई थी कोई भी नहीं सोचा था कि "लेकिन कुछ दिन, हर किसी के लिए इस का उपयोग करने के लिए जा रहा है सब कुछ दो बातें है कि, और कोई भी पता चलेगा कि कौन किसी के कोड का मतलब "।
जेसन सी

2
@ शिवेन ओह, निश्चित रूप से। फिर भी, यह बहुत बुरी चीजें हैं जैसे mapपुनरावृत्तियों वैध अपवाद नहीं हैं। ("पहला" = कुंजी और "दूसरा" = मूल्य ... वास्तव में std? वास्तव में?)
जेसन सी

जवाबों:


33

विकल्प 1 छोटे "केवल एक बार उपयोग की जाने वाली" चीजों के लिए ठीक है। अनिवार्य रूप std::pairसे अभी भी एक संरचना है। जैसा कि इस टिप्पणी विकल्प द्वारा कहा गया है 1 खरगोश के छेद की तरह कहीं नीचे वास्तव में बदसूरत कोड का नेतृत्व करेगा thing.second->first.second->secondऔर कोई भी वास्तव में इसे समझना नहीं चाहता है।

विकल्प 2 सब कुछ के लिए बेहतर है, क्योंकि यह पढ़ना आसान है कि नक्शे में चीजों का अर्थ क्या है। यदि आप डेटा बदलना चाहते हैं तो यह अधिक लचीला भी है (उदाहरण के लिए जब Ente को अचानक दूसरे ध्वज की आवश्यकता है)। प्रदर्शन यहां एक मुद्दा नहीं होना चाहिए।


15

प्रदर्शन :

निर्भर करता है।

आपके विशेष मामले में कोई प्रदर्शन अंतर नहीं होगा क्योंकि दोनों को समान रूप से स्मृति में रखा जाएगा।

बहुत विशिष्ट मामले में (यदि आप डेटा सदस्यों में से एक के रूप में एक खाली संरचना का उपयोग कर रहे थे ) तो std::pair<>संभावित रूप से खाली आधार अनुकूलन (ईबीओ) का उपयोग कर सकता है और संरचना के समकक्ष से कम आकार का हो सकता है। और कम आकार का आम तौर पर उच्च प्रदर्शन का मतलब है:

struct Empty {};
struct Thing { std::string name; Empty e; };

int main() {
    std::cout << sizeof(std::string) << "\n";
    std::cout << sizeof(std::tuple<std::string, Empty>) << "\n";
    std::cout << sizeof(std::pair<std::string, Empty>) << "\n";
    std::cout << sizeof(Thing) << "\n";
}

प्रिंट्स: 32, 32, 40, 40 आइडोन पर ।

नोट: मुझे किसी भी कार्यान्वयन के बारे में पता नहीं है जो वास्तव में नियमित जोड़े के लिए ईबीओ ट्रिक का उपयोग करता है, हालांकि यह आमतौर पर ट्यूपल्स के लिए उपयोग किया जाता है।


पठनीयता :

हालांकि, माइक्रो-ऑप्टिमाइज़ेशन के अलावा, एक नामित संरचना अधिक एर्गोनोमिक है।

मेरा मतलब है, map[k].firstकि बुरा नहीं है जबकि get<0>(map[k])मुश्किल से समझदारी है। इसके विपरीत map[k].nameजो तुरंत इंगित करता है कि हम क्या पढ़ रहे हैं।

यह सभी अधिक महत्वपूर्ण है जब प्रकार एक दूसरे के लिए परिवर्तनीय होते हैं, क्योंकि उन्हें अनजाने में स्वैप करने के बाद एक वास्तविक चिंता बन जाती है।

आप स्ट्रक्चरल बनाम नॉमिनल टाइपिंग के बारे में भी पढ़ना चाह सकते हैं। Enteएक विशेष प्रकार का है कि केवल चीजें हैं जो उम्मीद से पर संचालित किया जा सकता है Ente, कुछ भी है कि पर काम कर सकते हैं std::pair<std::string, bool>उन पर काम कर सकते हैं ... यहां तक कि जब std::stringया boolशामिल नहीं है वे क्या उम्मीद है, क्योंकि std::pairकोई है अर्थ विज्ञान इसके साथ जुड़े।


रखरखाव :

रखरखाव के मामले में, pairसबसे खराब है। आप एक फ़ील्ड नहीं जोड़ सकते।

tupleउस संबंध में बेहतर मेलों, जब तक आप नए क्षेत्र को जोड़ते हैं तब तक सभी मौजूदा फ़ील्ड एक ही सूचकांक द्वारा एक्सेस किए जाते हैं। जो पहले की तरह ही असंवेदनशील है लेकिन कम से कम आपको उन्हें अपडेट करने की जरूरत नहीं है।

structस्पष्ट विजेता है। आप जहां भी मन करे वहां फ़ील्ड जोड़ सकते हैं।


निष्कर्ष के तौर पर:

  • pair दोनों दुनिया का सबसे बुरा है,
  • tuple बहुत विशिष्ट मामले में थोड़ी बढ़त हो सकती है (खाली प्रकार),
  • उपयोग करेंstruct

नोट: यदि आप गेटर्स का उपयोग करते हैं, तो आप ग्राहकों के बिना खाली आधार चाल का उपयोग कर सकते हैं, जैसा कि इसके बारे में पता नहीं है struct Thing: Empty { std::string name; }; यही कारण है कि एनकैप्सुलेशन अगला विषय है जिसे आपको अपने आप से चिंतित होना चाहिए।


3
यदि आप मानक का पालन कर रहे हैं, तो आप जोड़े के लिए ईबीओ का उपयोग नहीं कर सकते। जोड़ी के तत्वों को सदस्यों में संग्रहीत किया जाता है firstऔर second, किक करने के लिए खाली आधार अनुकूलन के लिए कोई जगह नहीं है ।
Revolver_Ocelot

2
@Revolver_Ocelot: ठीक है, आप एक C ++ नहीं लिख सकते हैं pairजो EBO का उपयोग करेगा, लेकिन एक संकलक एक अंतर्निहित प्रदान कर सकता है। चूँकि वे सदस्य होने वाले हैं, हालाँकि, यह देखने योग्य हो सकता है (उनके पते की जाँच करना, उदाहरण के लिए) जिस स्थिति में यह अनुरूप नहीं होगा।
मैथ्यू एम।

1
C ++ 20 जोड़ता है [[no_unique_address]], जो सदस्यों के लिए EBO के समकक्ष सक्षम करता है।
अंडरस्कोर_ड

3

Pair सबसे अधिक चमकता है जब एक प्रकार का फ़ंक्शन के रूप में उपयोग किया जाता है साथ में std :: टाई और C ++ 17 की संरचित बाइंडिंग का उपयोग करके विनाशकारी असाइनमेंट के साथ। एसटीडी :: टाई का उपयोग करना:

struct Ente {/*...*/};
std::map<int, Ente> map;
auto inserted_position = map.end();
auto was_inserted = false;
std::tie(inserted_position, was_inserted) = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

C ++ 17 की संरचित बाइंडिंग का उपयोग करना:

struct Ente {/*...*/};
std::map<int, Ente> map;
auto [inserted_position, was_inserted] = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

एक std :: pair (या टपल) के उपयोग का एक बुरा उदाहरण कुछ इस तरह होगा:

using player_data = std::tuple<std::string, uint64_t, double>;
player_data player{};
/* ... */
auto health = std::get<2>(player);
/* ... */

क्योंकि यह स्पष्ट नहीं है जब std :: get <2> (player_data) को स्थिति सूचकांक में संग्रहीत किया जाता है। 2. पठनीयता याद रखें और पाठक के लिए यह स्पष्ट करना कि कोड क्या कर रहा है महत्वपूर्ण है । गौर करें कि यह बहुत अधिक पठनीय है:

struct player_data
{
    std::string name;
    uint64_t player_id;
    double current_health;
};
player_data player{};
/* ... */
auto health = player.current_health;
/* ... */

सामान्य तौर पर आपको std :: pair और std :: tuple के बारे में एक फ़ंक्शन से 1 से अधिक ऑब्जेक्ट को वापस करने के तरीकों के बारे में सोचना चाहिए। अंगूठे का नियम जो मैं उपयोग करता हूं (और कई अन्य लोगों को भी उपयोग करते देखा है) यह है कि वस्तुओं को एक std :: tuple या std :: pair में लौटाया जाता है, केवल एक फ़ंक्शन को कॉल करने के संदर्भ में "संबंधित" हैं जो उन्हें लौटाता है या डेटा संरचना के संदर्भ में जो उन्हें एक साथ जोड़ता है (जैसे std :: map उपयोग std :: pair इसके भंडारण प्रकार के लिए)। यदि संबंध आपके कोड में कहीं और मौजूद है तो आपको एक संरचना का उपयोग करना चाहिए।

कोर दिशानिर्देशों के संबंधित खंड:

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