आपको अग्रेषण समस्या को समझना होगा। आप पूरी समस्या को विस्तार से पढ़ सकते हैं , लेकिन मैं संक्षेप में बताऊंगा।
मूल रूप से, अभिव्यक्ति को देखते हुए E(a, b, ... , c)
, हम चाहते हैं कि अभिव्यक्ति f(a, b, ... , c)
समतुल्य हो। C ++ 03 में, यह असंभव है। कई प्रयास हैं, लेकिन वे सभी किसी न किसी संबंध में विफल हैं।
सबसे सरल है एक अंतराल-संदर्भ का उपयोग करना:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c)
{
E(a, b, c);
}
लेकिन यह अस्थायी मानों को संभालने में विफल रहता है: f(1, 2, 3);
क्योंकि वे एक अंतराल-संदर्भ के लिए बाध्य नहीं हो सकते हैं।
अगला प्रयास हो सकता है:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(a, b, c);
}
जो उपरोक्त समस्या को ठीक करता है, लेकिन फ्लॉप हो जाता है। यह अब E
नॉन-कास्ट तर्क देने की अनुमति देने में विफल है :
int i = 1, j = 2, k = 3;
void E(int&, int&, int&); f(i, j, k); // oops! E cannot modify these
तीसरा प्रयास कॉन्स्टेंस-रेफरेंस को स्वीकार करता है, लेकिन फिर const_cast
वह const
दूर है:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(const_cast<A&>(a), const_cast<B&>(b), const_cast<C&>(c));
}
यह सभी मूल्यों को स्वीकार करता है, सभी मूल्यों को पारित कर सकता है, लेकिन संभावित रूप से अपरिभाषित व्यवहार की ओर जाता है:
const int i = 1, j = 2, k = 3;
E(int&, int&, int&); f(i, j, k); // ouch! E can modify a const object!
एक अंतिम समाधान सब कुछ सही ढंग से संभालता है ... बनाए रखने की असंभवता की कीमत पर। आप कब्ज और गैर-कास्ट के सभी संयोजनों के f
साथ, ओवरलोड प्रदान करते हैं :
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c);
एन तर्कों को 2 एन संयोजन, एक दुःस्वप्न की आवश्यकता होती है । हम यह स्वचालित रूप से करना चाहते हैं।
(यह प्रभावी रूप से हमें C ++ 11 में हमारे लिए करने के लिए कंपाइलर है।)
C ++ 11 में, हमें इसे ठीक करने का मौका मिलता है। एक समाधान मौजूदा प्रकारों पर टेम्पलेट कटौती नियमों को संशोधित करता है, लेकिन यह संभावित रूप से कोड का एक बड़ा हिस्सा तोड़ देता है। इसलिए हमें दूसरा रास्ता खोजना होगा।
समाधान इसके बजाय नए जोड़े गए प्रतिद्वंद्वियों-संदर्भों का उपयोग करना है ; जब हम रेवल्यू-रेफरेंस प्रकारों को घटाते हैं और कोई वांछित परिणाम बनाते हैं, तो हम नए नियम पेश कर सकते हैं। आखिरकार, हम अब कोड को संभवतः नहीं तोड़ सकते।
यदि एक संदर्भ के संदर्भ में दिया जाता है (नोट संदर्भ एक अर्थपूर्ण शब्द है जिसका अर्थ दोनों है T&
और T&&
), हम परिणाम के प्रकार का पता लगाने के लिए निम्नलिखित नियम का उपयोग करते हैं:
"[दिया गया] एक टाइप टीआर जो कि टाइप टी का एक संदर्भ है, टाइप करने का प्रयास" सीवी टीआर के लिए लवल्यू संदर्भ "टाइप बनाता है" टी के लिए लैवल्यू संदर्भ ", जबकि टाइप बनाने का प्रयास" रेवल्यू रेफरेंस " cv TR "TR प्रकार बनाता है।"
या सारणीबद्ध रूप में:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
अगला, टेम्पलेट लॉजिक डिडक्शन के साथ: यदि कोई तर्क एक लैवल्यू A है, तो हम ए को लॉवल्यू रेफरेंस के साथ टेम्प्लेट तर्क की आपूर्ति करते हैं। अन्यथा, हम सामान्य रूप से कटौती करते हैं। यह तथाकथित सार्वभौमिक संदर्भ देता है (शब्द अग्रेषण संदर्भ अब आधिकारिक है)।
यह क्यों उपयोगी है? क्योंकि संयुक्त हम एक प्रकार के मूल्य श्रेणी का ट्रैक रखने की क्षमता रखते हैं: यदि यह एक अंतराल था, तो हमारे पास एक अंतराल-संदर्भ पैरामीटर है, अन्यथा हमारे पास एक अंतराल-संदर्भ पैरामीटर है।
कोड में:
template <typename T>
void deduce(T&& x);
int i;
deduce(i); // deduce<int&>(int& &&) -> deduce<int&>(int&)
deduce(1); // deduce<int>(int&&)
आखिरी चीज चर के मूल्य श्रेणी को "आगे" करना है। ध्यान रखें, एक बार फ़ंक्शन के अंदर पैरामीटर को किसी भी चीज के लिए एक अंतराल के रूप में पारित किया जा सकता है:
void foo(int&);
template <typename T>
void deduce(T&& x)
{
foo(x); // fine, foo can refer to x
}
deduce(1); // okay, foo operates on x which has a value of 1
यह अच्छा नहीं है। E को उसी प्रकार का मूल्य-श्रेणी प्राप्त करने की आवश्यकता है जो हमें मिला है! समाधान यह है:
static_cast<T&&>(x);
यह क्या करता है? विचार करें कि हम deduce
फ़ंक्शन के अंदर हैं , और हमें एक अंतराल दिया गया है। इसका मतलब है T
कि एक है A&
, और इसलिए स्थिर कलाकारों के लिए लक्ष्य प्रकार है A& &&
, या बस A&
। चूंकि x
पहले से ही एक है A&
, हम कुछ भी नहीं करते हैं और एक अंतराल संदर्भ के साथ छोड़ दिया जाता है।
जब हम एक प्रतिद्वंद्विता पारित किया गया T
है A
, तो स्थिर कलाकारों के लिए लक्ष्य प्रकार है A&&
। कास्ट एक परिणामी अभिव्यक्ति का परिणाम है, जिसे अब एक अंतराल संदर्भ में पारित नहीं किया जा सकता है । हमने पैरामीटर का मान श्रेणी बनाए रखा है।
इन्हें एक साथ रखने से हमें "पूर्ण अग्रेषण" मिलता है:
template <typename A>
void f(A&& a)
{
E(static_cast<A&&>(a));
}
जब f
एक लैवल्यू प्राप्त होता है, एक लैवल्यू प्राप्त करता है E
। जब f
एक प्रतिद्वंद्विता प्राप्त करता है, एक प्रतिद्वंद्विता प्राप्त करता है E
। उत्तम।
और निश्चित रूप से, हम बदसूरत से छुटकारा चाहते हैं। static_cast<T&&>
याद रखने में अजीब और अजीब है; बजाय इसके कि यूटिलिटी फंक्शन बनाया जाए forward
, जो समान कार्य करता है:
std::forward<A>(a);
// is the same as
static_cast<A&&>(a);
f
एक समारोह नहीं होगा , और एक अभिव्यक्ति नहीं होगी?