सबसे पहले, "यह * के लिए रेफरी-क्वालीफायर" सिर्फ एक "मार्केटिंग स्टेटमेंट" है। *this
कभी नहीं बदलता प्रकार , इस पोस्ट के नीचे देखें। हालांकि इसे इस शब्द के साथ समझना आसान है।
इसके बाद, निम्न कोड चुनता कार्य के आधार पर कहा जा रेफरी-क्वालीफायर समारोह के "अंतर्निहित वस्तु पैरामीटर" का † :
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
आउटपुट:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
पूरी बात आपको इस तथ्य का लाभ उठाने की अनुमति देने के लिए की जाती है जब ऑब्जेक्ट को कॉल किया जाता है, एक प्रतिद्वंद्विता है (उदाहरण के लिए, अस्थायी नाम)। एक और उदाहरण के रूप में निम्नलिखित कोड लें:
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
यह थोड़ा विरोधाभास हो सकता है, लेकिन आपको विचार प्राप्त करना चाहिए।
ध्यान दें कि आप सीवी-क्वालिफायर ( const
और volatile
) और रेफ-क्वालिफायर ( &
और &&
) को जोड़ सकते हैं ।
नोट: कई मानक उद्धरण और अधिभार संकल्प स्पष्टीकरण यहाँ के बाद!
Ic यह समझने के लिए कि यह कैसे काम करता है, और क्यों @Nicol Bolas का उत्तर कम से कम आंशिक रूप से गलत है, हमें C ++ मानक में थोड़ा सा खोदना होगा (यह समझाने वाला भाग @ निकोल का उत्तर गलत क्यों है? केवल उस में रुचि)।
किस फ़ंक्शन को कहा जा रहा है, इसे अधिभार संकल्प नामक एक प्रक्रिया द्वारा निर्धारित किया जाता है । यह प्रक्रिया काफी जटिल है, इसलिए हम केवल उस बिट को स्पर्श करेंगे जो हमारे लिए महत्वपूर्ण है।
सबसे पहले, यह देखना महत्वपूर्ण है कि सदस्य कार्यों के लिए अधिभार संकल्प कैसे काम करता है:
§13.3.1 [over.match.funcs]
P2 उम्मीदवार के कार्यों के सेट में सदस्य और गैर-सदस्य कार्य दोनों को एक ही तर्क सूची के विरुद्ध हल किया जा सकता है। इसलिए कि इस विषम सेट के भीतर तर्क और पैरामीटर सूचियां तुलनीय हैं, एक सदस्य फ़ंक्शन को एक अतिरिक्त पैरामीटर माना जाता है, जिसे अंतर्निहित ऑब्जेक्ट पैरामीटर कहा जाता है, जो उस वस्तु का प्रतिनिधित्व करता है जिसके लिए सदस्य फ़ंक्शन को बुलाया गया है । [...]
p3 इसी प्रकार, उपयुक्त होने पर, संदर्भ एक तर्क सूची का निर्माण कर सकता है जिसमें ऑब्जेक्ट को संचालित करने के लिए निरूपित करने के लिए एक निहित ऑब्जेक्ट तर्क होता है।
हमें सदस्य और गैर-सदस्य कार्यों की तुलना करने की आवश्यकता क्यों है? ऑपरेटर ओवरलोडिंग, इसीलिए। इस पर विचार करो:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
आप निश्चित रूप से नि: शुल्क फ़ंक्शन को कॉल करने के लिए चाहते हैं, क्या आप नहीं?
char const* s = "free foo!\n";
foo f;
f << s;
यही कारण है कि सदस्य और गैर-सदस्य फ़ंक्शन तथाकथित ओवरलोड-सेट में शामिल हैं। संकल्प को कम जटिल बनाने के लिए, मानक उद्धरण का बोल्ड हिस्सा मौजूद है। इसके अतिरिक्त, यह हमारे लिए महत्वपूर्ण बिट है (एक ही खंड):
p4 गैर-स्थैतिक सदस्य फ़ंक्शन के लिए, अंतर्निहित ऑब्जेक्ट पैरामीटर का प्रकार है
X
वह वर्ग कहाँ है जो फ़ंक्शन का सदस्य है और cv सदस्य फ़ंक्शन की घोषणा पर cv-योग्यता है। [...]
p5 अधिभार के प्रस्ताव के दौरान [...] [t] वह ऑब्जेक्ट पैरामीटर को निहित करता है [...] इसी पहचान को बनाए रखता है क्योंकि संबंधित तर्क पर रूपांतरण इन अतिरिक्त नियमों का पालन करेगा:
[...]
(अंतिम बिट का अर्थ है कि आप सदस्य के फ़ंक्शन (या ऑपरेटर को कॉल किया जाता है) के निहित रूपांतरण के आधार पर अधिभार संकल्प को धोखा नहीं दे सकते।)
आइए इस पोस्ट के शीर्ष पर पहला उदाहरण लेते हैं। उपर्युक्त परिवर्तन के बाद, अधिभार-सेट कुछ इस तरह दिखता है:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
तब तर्क सूची, जिसमें एक निहित ऑब्जेक्ट तर्क होता है , को अधिभार-सेट में निहित प्रत्येक फ़ंक्शन के पैरामीटर-सूची के विरुद्ध मिलान किया जाता है। हमारे मामले में, तर्क सूची में केवल वही ऑब्जेक्ट तर्क होगा। आइए देखें कि यह कैसा दिखता है:
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
यदि, सेट में सभी अधिभार के बाद परीक्षण किया जाता है, तो केवल एक ही शेष रहता है, अधिभार संकल्प सफल हुआ और उस परिवर्तित अधिभार से जुड़े कार्य को कहा जाता है। वही दूसरी कॉल के लिए 'f' जाता है:
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
नोट तथापि कि, हम किसी भी मुहैया कराई थी नहीं रेफरी-क्वालीफायर (और इस तरह के रूप में कार्य ओवरलोड न हो), कि f1
होगा एक rvalue (अभी भी मेल खाते हैं §13.3.1
):
p5 [...] गैर-स्थैतिक सदस्य कार्यों के लिए रेफरी-क्वालीफायर के बिना घोषित , एक अतिरिक्त नियम लागू होता है:
- भले ही अंतर्निहित ऑब्जेक्ट पैरामीटर
const
-क्वालिफ़ाइड नहीं है , फिर भी एक प्रतिद्वंद्विता पैरामीटर के लिए बाध्य हो सकती है जब तक कि अन्य सभी मामलों में तर्क को अंतर्निहित ऑब्जेक्ट पैरामीटर के प्रकार में परिवर्तित किया जा सकता है।
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
अब, क्यों @ निकोल का जवाब कम से कम गलत है। वह कहता है:
ध्यान दें कि यह घोषणा प्रकार बदलता है *this
।
यह गलत है, *this
है हमेशा एक lvalue:
§5.3.1 [expr.unary.op] p1
यूनिरी *
ऑपरेटर अप्रत्यक्ष प्रदर्शन करता है : जिस अभिव्यक्ति पर इसे लागू किया जाता है वह एक ऑब्जेक्ट प्रकार के लिए एक संकेतक होगा, या एक फ़ंक्शन प्रकार के लिए एक संकेतक होगा और परिणाम वस्तु या फ़ंक्शन का एक अंतराल है जो अभिव्यक्ति को इंगित करता है।
§9.3.2 [class.this] p1
एक गैर-स्थिर (9.3) सदस्य फ़ंक्शन के शरीर में, कीवर्ड this
एक प्रचलित अभिव्यक्ति है जिसका मूल्य उस ऑब्जेक्ट का पता है जिसके लिए फ़ंक्शन कहा जाता है। के प्रकार के this
एक वर्ग के एक सदस्य समारोह में X
है X*
। [...]