Cort Ammon का स्वीकृत उत्तर अच्छा है, लेकिन मुझे लगता है कि कार्यान्वयन के बारे में एक और महत्वपूर्ण बिंदु है।
मान लीजिए कि मेरी दो अलग-अलग अनुवाद इकाइयाँ हैं, "one.cpp" और "two.cpp"।
struct A { int operator()(int x) const { return x+1; } };
auto b = [](int x) { return x+1; };
using A1 = A;
using B1 = decltype(b);
extern void foo(A1);
extern void foo(B1);
foo
एक ही पहचानकर्ता ( foo
) का उपयोग करने के दो अधिभार, लेकिन अलग-अलग नाम हैं। (POSIX-ish सिस्टम पर उपयोग किए जाने वाले इटेनियम एबी _Z3foo1A
में, इस विशेष मामले में, गिरे हुए नाम हैं और _Z3fooN1bMUliE_E
।)
struct A { int operator()(int x) const { return x + 1; } };
auto b = [](int x) { return x + 1; };
using A2 = A;
using B2 = decltype(b);
void foo(A2) {}
void foo(B2) {}
सी ++ संकलक को यह सुनिश्चित करना चाहिए कि void foo(A1)
"दो.cpp" में मंगला नाम "one.cpp" के मंगली नाम के समान हो extern void foo(A2)
, ताकि हम दो ऑब्जेक्ट फ़ाइलों को एक साथ लिंक कर सकें। यह दो प्रकार का भौतिक अर्थ है "एक ही प्रकार": यह अनिवार्य रूप से अलग-अलग संकलित ऑब्जेक्ट फ़ाइलों के बीच एबीआई-संगतता के बारे में है।
यह सुनिश्चित करने के लिए सी ++ संकलक की आवश्यकता नहीं है B1
और B2
"समान प्रकार" हैं। (वास्तव में, यह सुनिश्चित करना आवश्यक है कि वे अलग-अलग प्रकार के हों; लेकिन यह अभी उतना महत्वपूर्ण नहीं है।)
यह सुनिश्चित करने के लिए संकलक किस भौतिक तंत्र का उपयोग करता है A1
और A2
"समान प्रकार" हैं?
यह बस टाइपसेफ के माध्यम से दफन करता है, और फिर प्रकार के पूरी तरह से योग्य नाम को देखता है। यह एक वर्ग प्रकार का नाम है A
। (कुंआ,::A
चूंकि यह वैश्विक नामस्थान में है।) इसलिए यह दोनों मामलों में एक ही प्रकार का है। यह समझना आसान है। इससे भी महत्वपूर्ण बात, इसे लागू करना आसान है । यह देखने के लिए कि क्या दो प्रकार के वर्ग समान हैं, आप उनके नाम लेते हैं और एक करते हैं strcmp
। किसी वर्ग प्रकार को किसी फ़ंक्शन के मांगे गए नाम से जोड़ने के लिए, आप उसके नाम में वर्णों की संख्या लिखते हैं, उसके बाद उन वर्णों को लिखते हैं।
तो, नाम प्रकार आसान करने के लिए आसान कर रहे हैं।
यह सुनिश्चित करने के लिए कंपाइलर किस भौतिक तंत्र का उपयोग कर सकता हैB1
B2
हैं और "उसी प्रकार के" हैं, काल्पनिक दुनिया में जहां C ++ के लिए उन्हें उसी प्रकार का होना आवश्यक था?
खैर, यह प्रकार के नाम का उपयोग नहीं कर सका, क्योंकि प्रकार नहीं करता है में नाम है ।
हो सकता है कि यह किसी तरह लंबोदर के शरीर के पाठ को एनकोड कर सके । लेकिन यह एक तरह से अजीब होगा, क्योंकि वास्तव b
में "one.cpp" "दो.cpp" से बिल्कुल अलग है b
: "one.cpp" में x+1
और "two.cpp" है x + 1
। इसलिए हमें एक नियम के साथ आना होगा जो या तो कहता है कि यह व्हाट्सएप अंतर कोई फर्क नहीं पड़ता है, या यह है कि यह (उन्हें विभिन्न प्रकार के सभी बनाने के बाद), या हो सकता है कि यह (शायद कार्यक्रम की वैधता कार्यान्वयन-परिभाषित है) , या हो सकता है कि यह "बीमार-गठित कोई नैदानिक आवश्यक नहीं है")। वैसे भी,A
कठिनाई से सबसे आसान तरीका बस यह कहना है कि प्रत्येक लैम्ब्डा अभिव्यक्ति एक अद्वितीय प्रकार के मूल्यों का उत्पादन करती है। फिर विभिन्न अनुवाद इकाइयों में परिभाषित दो लंबोदर प्रकार निश्चित रूप से एक ही प्रकार के नहीं होते हैं । एक एकल अनुवाद इकाई के भीतर, हम स्रोत कोड की शुरुआत से गिनती करके "नाम" लंबोदर प्रकार कर सकते हैं:
auto a = [](){};
auto b = [](){};
auto f(int x) {
return [x](int y) { return x+y; };
}
auto g(float x) {
return [x](int y) { return x+y; };
}
बेशक इन नामों का अर्थ केवल इस अनुवाद इकाई के भीतर है। यह TU's $_0
हमेशा कुछ अन्य TU से अलग प्रकार का होता है $_0
, भले ही यह TU struct A
हमेशा की तरह कुछ अन्य TU का है struct A
।
वैसे, ध्यान दें कि हमारे "लंबो के पाठ को सांकेतिक शब्दों में बदलना" विचार की एक और सूक्ष्म समस्या थी: लंबोदा $_2
और $_3
जिसमें एक ही पाठ शामिल है , लेकिन उन्हें स्पष्ट रूप से एक ही प्रकार नहीं माना जाना चाहिए !
वैसे, सी ++ पता करने के लिए एक मनमाना सी ++ के पाठ वध करने के लिए कैसे संकलक की आवश्यकता होती है अभिव्यक्ति , के रूप में
template<class T> void foo(decltype(T())) {}
template void foo<int>(int);
लेकिन C ++ को (अभी तक) संकलक को यह जानने की आवश्यकता नहीं है कि मनमाने ढंग से C ++ स्टेटमेंट को कैसे मेनटेन किया जाए । decltype([](){ ...arbitrary statements... })
अभी भी C ++ 20 में बीमार है।
यह भी ध्यान दें कि किसी अनजान प्रकार का उपयोग करके स्थानीय उपनाम देना आसान है typedef
/ using
। मुझे लग रहा है कि आपका सवाल कुछ ऐसा करने की कोशिश करने से पैदा हो सकता है जो इस तरह से हल किया जा सकता है।
auto f(int x) {
return [x](int y) { return x+y; };
}
using AdderLambda = decltype(f(0));
int of_one(AdderLambda g) { return g(1); }
int main() {
auto f1 = f(1);
assert(of_one(f1) == 2);
auto f42 = f(42);
assert(of_one(f42) == 43);
}
EDD TO ADD: अन्य उत्तरों पर आपकी कुछ टिप्पणियों को पढ़ने से, ऐसा लगता है कि आप सोच रहे हैं कि क्यों
int add1(int x) { return x + 1; }
int add2(int x) { return x + 2; }
static_assert(std::is_same_v<decltype(add1), decltype(add2)>);
auto add3 = [](int x) { return x + 3; };
auto add4 = [](int x) { return x + 4; };
static_assert(not std::is_same_v<decltype(add3), decltype(add4)>);
ऐसा इसलिए है क्योंकि कैप्चरलेस लैम्ब्डा डिफ़ॉल्ट-कंस्ट्रक्टेबल हैं। (C ++ में केवल C ++ 20 के रूप में, लेकिन यह हमेशा वैचारिक रूप से सत्य रहा है।)
template<class T>
int default_construct_and_call(int x) {
T t;
return t(x);
}
assert(default_construct_and_call<decltype(add3)>(42) == 45);
assert(default_construct_and_call<decltype(add4)>(42) == 46);
यदि आपने कोशिश की है default_construct_and_call<decltype(&add1)>
, t
तो एक डिफ़ॉल्ट-आरंभिक फ़ंक्शन पॉइंटर होगा और आप शायद सेगफ़ॉल्ट होंगे। वह, जैसे, उपयोगी नहीं है।