Std :: टाई काम कैसे करता है?


120

मैंने std::tieइसमें ज्यादा विचार दिए बिना उपयोग किया है। यह काम करता है इसलिए मैंने अभी स्वीकार किया है:

auto test()
{
   int a, b;
   std::tie(a, b) = std::make_tuple(2, 3);
   // a is now 2, b is now 3
   return a + b; // 5
}

लेकिन यह काला जादू कैसे काम करता है ? एक अस्थायी std::tieपरिवर्तन से कैसे बनता है aऔर b? मुझे यह अधिक दिलचस्प लगता है क्योंकि यह एक पुस्तकालय सुविधा है, न कि एक भाषा सुविधा, इसलिए निश्चित रूप से यह कुछ ऐसा है जिसे हम खुद को लागू कर सकते हैं और समझ सकते हैं।

जवाबों:


152

मुख्य अवधारणा को स्पष्ट करने के लिए, आइए इसे और अधिक मूल उदाहरण में कम करें। यद्यपि std::tieअधिक मान देने वाले कार्यों के लिए उपयोगी है (अधिक), हम इसे केवल एक मान के साथ ठीक समझ सकते हैं:

int a;
std::tie(a) = std::make_tuple(24);
return a; // 24

आगे बढ़ने के लिए हमें जिन चीजों की जानकारी होनी चाहिए:

  • std::tie निर्माण और संदर्भों की एक वापसी देता है।
  • std::tuple<int>और std::tuple<int&>उन दोनों के बीच कोई संबंध नहीं के साथ 2 पूरी तरह से अलग कक्षाएं, अन्य है कि वे समान टेम्पलेट से जनरेट किए गए थे, कर रहे हैं std::tuple
  • tuple में operator=विभिन्न प्रकारों (लेकिन समान संख्या) का एक tuple स्वीकार होता है, जहां प्रत्येक सदस्य को व्यक्तिगत रूप से निर्दिष्ट किया जाता है - crevenent से :

    template< class... UTypes >
    tuple& operator=( const tuple<UTypes...>& other );

    (3) सभी मैं के लिए, प्रदान करती है std::get<i>(other)करने के लिए std::get<i>(*this)

अगला कदम उन कार्यों से छुटकारा पाना है जो केवल आपके रास्ते में आते हैं, इसलिए हम अपने कोड को इस में बदल सकते हैं:

int a;
std::tuple<int&>{a} = std::tuple<int>{24};
return a; // 24

अगला कदम यह देखना है कि वास्तव में उन संरचनाओं के अंदर क्या होता है। इसके लिए, मैं दो प्रकार के Tपदार्थ बनाता हूं, std::tuple<int>और Trप्रतिस्थापन के लिए std::tuple<int&>, हमारे कार्यों के लिए न्यूनतम न्यूनतम तक छीन लिया गया है:

struct T { // substituent for std::tuple<int>
    int x;
};

struct Tr { // substituent for std::tuple<int&>
    int& xr;

    auto operator=(const T& other)
    {
       // std::get<I>(*this) = std::get<I>(other);
       xr = other.x;
    }
};

auto foo()
{
    int a;
    Tr{a} = T{24};

    return a; // 24
}

और अंत में, मैं सभी को एक साथ संरचनाओं से छुटकारा पाना पसंद करता हूं (ठीक है, यह 100% समतुल्य नहीं है, लेकिन यह हमारे लिए काफी करीब है, और इसे अनुमति देने के लिए पर्याप्त स्पष्ट है):

auto foo()
{
    int a;

    { // block substituent for temporary variables

    // Tr{a}
    int& tr_xr = a;

    // T{24}
    int t_x = 24;

    // = (asignement)
    tr_xr = t_x;
    }

    return a; // 24
}

इसलिए मूल रूप से, std::tie(a)डेटा सदस्य संदर्भ को इनिशियलाइज़ करता है astd::tuple<int>(24)मान के साथ एक डेटा सदस्य बनाता है 24, और असाइनमेंट पहली संरचना में डेटा सदस्य संदर्भ में 24 असाइन करता है। लेकिन चूँकि वह डेटा सदस्य एक संदर्भ से जुड़ा होता है a, जो मूल रूप से असाइन 24होता है a


1
मुझे कौन सी समस्या है, यह है कि हम असाइनमेंट ऑपरेटर को कॉल कर रहे हैं।
एडम ज़हरान

में इस सवाल का जवाब है, यह कहा गया है कि एक कंटेनर एक संदर्भ को रोक नहीं सकते। tupleएक संदर्भ क्यों रखा जा सकता है?
nn0p

6
@ nn0p std::tupleएक कंटेनर नहीं है, कम से कम C ++ शब्दावली में नहीं, std::vectorपसंद और पसंद नहीं। उदाहरण के लिए, आप एक सामान्य से अधिक तरीके से पुनरावृत्ति नहीं कर सकते क्योंकि इसमें विभिन्न प्रकार की वस्तुएँ शामिल हैं।
बोलोव

@ अदम टाई (x, y) = make_pair (1,2); वास्तव में std :: टाई (x, y) .operator = (std :: make_pair (1, 2)) बन जाता है, इसीलिए "वर्क टू असाइनमेंट टू एवल्यू" वर्क XD
Ju Piece

30

यह आपके प्रश्न का किसी भी तरह से उत्तर नहीं देता है, लेकिन मुझे इसे वैसे भी पोस्ट करने दें क्योंकि C ++ 17 मूल रूप से तैयार है (संकलक समर्थन के साथ), इसलिए यह सोचते हुए कि पुराना सामान कैसे काम करता है, यह संभवतः वर्तमान, और कैसे देखने लायक है भविष्य, सी ++ का संस्करण भी काम करता है।

C ++ 17 के साथ आप संरचित बाइंडिंग के रूपstd::tie में बहुत अधिक खरोंच कर सकते हैं । वे ऐसा ही करते हैं (ठीक है, समान नहीं , लेकिन उनका समान शुद्ध प्रभाव है), हालांकि आपको कम वर्ण टाइप करने की आवश्यकता है, इसके लिए पुस्तकालय समर्थन की आवश्यकता नहीं है, और आपके पास संदर्भ लेने की क्षमता भी है, यदि ऐसा होता है आपको क्या चाहिए।

(ध्यान दें कि C ++ में 17 कंस्ट्रक्टर डिडक्शन डिडक्शन करते हैं, इसलिए make_tupleयह कुछ हद तक अतिश्योक्तिपूर्ण भी हो गया है।)

int a, b;
std::tie(a, b) = std::make_tuple(2, 3);

// C++17
auto  [c, d] = std::make_tuple(4, 5);
auto  [e, f] = std::tuple(6, 7);
std::tuple t(8,9); auto& [g, h] = t; // not possible with std::tie

2
अगर वह अंतिम पंक्ति संकलित करता है तो मैं थोड़ा चिंतित हूं। यह एक अस्थायी के संदर्भ को बांधने जैसा लगता है जो कि अवैध है।
नीर फ्रीडमैन

3
@ नील इसका या तो एक रेवल्यू रेफरेंस या एक कॉस्ट लैवल्यू रेफरेंस होना चाहिए। आप किसी प्रचलन (अस्थायी) के लिए एक अंतराल संदर्भ नहीं बाँध सकते। हालांकि यह उम्र के लिए MSVC में एक "विस्तार" रहा है।
नीर फ्रीडमैन

1
यह शायद यह भी ध्यान देने योग्य है कि इसके विपरीत tie, संरचित बाइंडिंग का उपयोग उन प्रकारों पर किया जा सकता है जो डिफ़ॉल्ट-निर्माण योग्य नहीं हैं।
दान

5
हाँ, std::tie()C ++ 17 के बाद से बहुत कम उपयोगी है, जहां संरचित बाइंडिंग आमतौर पर बेहतर होते हैं, लेकिन इसका अभी भी उपयोग होता है, जिसमें मौजूदा (एक साथ नए घोषित नहीं) वेरिएबल्स को असाइन करना और कई वेरिएबल्स या अन्य चीजों को स्वैप करने जैसे अन्य काम करना शामिल है संदर्भ के लिए असाइन करना चाहिए।
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.