auto&& var = <initializer>
आप का उपयोग करके आप कह रहे हैं: मैं किसी भी इनिशियलाइज़र को स्वीकार करूंगा चाहे वह एक लवल्यू या रिवाल्यू अभिव्यक्ति हो और मैं इसकी कमी को बनाए रखूंगा । यह आमतौर पर अग्रेषण (आमतौर पर T&&
) के लिए उपयोग किया जाता है । कारण यह काम करता है क्योंकि एक "सार्वभौमिक संदर्भ" है, auto&&
या T&&
, किसी भी चीज के लिए बाध्य होगा ।
आप कह सकते हैं, ठीक है कि क्यों न सिर्फ इसका उपयोग किया जाए const auto&
क्योंकि यह किसी भी चीज़ से जुड़ेगा? एक const
संदर्भ का उपयोग करने के साथ समस्या यह है कि यह है const
! आप बाद में इसे किसी भी गैर-कॉन्स्टेबल संदर्भ में बाँधने में सक्षम नहीं होंगे या किसी भी सदस्य फ़ंक्शन को चिह्नित नहीं करेंगे const
।
एक उदाहरण के रूप में, कल्पना करें कि आप एक प्राप्त करना चाहते हैं std::vector
, एक इटैलर को उसके पहले तत्व में ले जाएं और उस इट्रेटर द्वारा इंगित मूल्य को किसी तरह से संशोधित करें:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
यह कोड आरंभिक अभिव्यक्ति की परवाह किए बिना बस ठीक संकलन करेगा। auto&&
निम्नलिखित तरीकों से विफल होने के विकल्प :
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
तो इसके लिए, auto&&
पूरी तरह से काम करता है! auto&&
इस तरह का उपयोग करने का एक उदाहरण रेंज-आधारित for
लूप में है। देखें मेरे दूसरे प्रश्न अधिक जानकारी के लिए।
यदि आप इस तथ्य को संरक्षित करने के लिए std::forward
अपने auto&&
संदर्भ का उपयोग करते हैं कि यह मूल रूप से या तो एक लवल्यू या एक प्रतिद्वंद्विता है, तो आपका कोड कहता है: अब जब मुझे आपकी वस्तु या तो एक लवल्यू या रूवल अभिव्यक्ति से मिली है, तो मैं इसे मूल रूप से पूर्णता मूल्य संरक्षित करना चाहता हूं। इसलिए मैंने इसे सबसे कुशलता से उपयोग किया - यह इसे अमान्य कर सकता है। जैसे की:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
use_it_elsewhere
जब मूल आरंभीकरण एक मामूली परिवर्तन था, तो यह प्रदर्शन (प्रतियों से बचना) के लिए अपनी हिम्मत को चीरने की अनुमति देता है।
इसका क्या मतलब है कि क्या हम या जब हम संसाधनों को चुरा सकते हैं var
? अच्छी तरह से auto&&
कुछ भी करने के लिए बाध्य हो जाएगा, हम संभवतः var
खुद को हिम्मत से चीरने की कोशिश नहीं कर सकते - यह बहुत अच्छी तरह से एक अंतराल या यहां तक कि कब्ज हो सकता है। लेकिन हम std::forward
इसे अन्य कार्यों के लिए कर सकते हैं जो पूरी तरह से इसके अंदरूनी हिस्सों को तबाह कर सकते हैं। जैसे ही हम ऐसा करते हैं, हमें var
अमान्य स्थिति में होना चाहिए ।
अब इसे इस मामले पर लागू करते हैं auto&& var = foo();
, जैसा कि आपके प्रश्न में दिया गया है, जहां फू एक T
मूल्य के आधार पर रिटर्न करता है। इस मामले में हम निश्चित रूप से जानते हैं कि किस प्रकार की var
कटौती की जाएगी T&&
। जब से हम कुछ के लिए जानते हैं कि यह एक प्रतिद्वंद्विता है, हमें std::forward
इसके संसाधनों को चोरी करने की अनुमति की आवश्यकता नहीं है। इस विशिष्ट मामले में, यह जानते हुए कि foo
मूल्य से रिटर्न होता है , पाठक को इसे बस इस रूप में पढ़ना चाहिए: मैं अस्थायी रूप से लौटाए गए समय का संदर्भ ले रहा हूं foo
, इसलिए मैं खुशी से इससे आगे बढ़ सकता हूं।
एक परिशिष्ट के रूप में, मुझे लगता है कि यह तब ध्यान देने योग्य है जब एक अभिव्यक्ति जैसी some_expression_that_may_be_rvalue_or_lvalue
स्थिति बदल सकती है, एक "अच्छी तरह से आपका कोड बदल सकता है" स्थिति के अलावा। तो यहाँ एक उदाहरण है:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
यहाँ, get_vector<T>()
वह प्यारी अभिव्यक्ति है जो सामान्य प्रकार के आधार पर या तो एक लवलीन या लकीर हो सकती है T
। हम अनिवार्य रूप get_vector
से टेम्पलेट पैरामीटर के माध्यम से रिटर्न प्रकार बदलते हैं foo
।
जब हम कॉल करते हैं foo<std::vector<int>>
, get_vector
तो global_vec
मूल्य से वापस आ जाएगा , जो एक व्याकुल अभिव्यक्ति देता है। वैकल्पिक रूप से, जब हम कहते हैं foo<std::vector<int>&>
, get_vector
वापस आ जाएगी global_vec
, संदर्भ द्वारा एक lvalue अभिव्यक्ति हो जाती है।
यदि हम करें तो:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
हमें उम्मीद के अनुसार निम्न आउटपुट मिले:
2
1
2
2
आप बदलना थे, तो auto&&
में से किसी को कोड में auto
, auto&
, const auto&
, या const auto&&
फिर हम परिणाम हम चाहते हैं नहीं मिलेगा।
प्रोग्राम लॉजिक को बदलने के लिए एक वैकल्पिक तरीका यह है कि क्या आपके auto&&
संदर्भ को एक लवल्यू या रैवल्यू एक्सप्रेशन के साथ आरम्भ किया गया है:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
auto&&
? मैं यह देखने के बारे में सोच रहा हूं कि लूप के लिए एक श्रेणी-आधारितauto&&
एक उदाहरण के रूप में उपयोग करने के लिए क्यों फैलता है , लेकिन इसके लिए गोल नहीं मिला है। शायद जो कोई भी इसका जवाब दे सकता है।