`Is_base_of` कैसे काम करता है?


118

निम्नलिखित कोड कैसे काम करता है?

typedef char (&yes)[1];
typedef char (&no)[2];

template <typename B, typename D>
struct Host
{
  operator B*() const;
  operator D*();
};

template <typename B, typename D>
struct is_base_of
{
  template <typename T> 
  static yes check(D*, T);
  static no check(B*, int);

  static const bool value = sizeof(check(Host<B,D>(), int())) == sizeof(yes);
};

//Test sample
class Base {};
class Derived : private Base {};

//Expression is true.
int test[is_base_of<Base,Derived>::value && !is_base_of<Derived,Base>::value];
  1. ध्यान दें कि Bनिजी आधार है। यह कैसे काम करता है?

  2. ध्यान दें कि operator B*()कास्ट है। यह महत्वपूर्ण क्यों है?

  3. से template<typename T> static yes check(D*, T);बेहतर क्यों है static yes check(B*, int);?

नोट : यह घटा हुआ संस्करण है (मैक्रोज़ हटा दिए गए हैं) boost::is_base_of। और यह कंपाइलरों की विस्तृत श्रृंखला पर काम करता है।


4
टेम्प्लेट पैरामीटर और एक सच्चे वर्ग नाम के लिए समान पहचानकर्ता का उपयोग करना आपके लिए बहुत उलझन की बात है ...
Matthieu M.

1
@ मैथ्यू एम।, मैं इसे सही करने के लिए खुद पर ले गया हूं :)
किरिल वी। ल्याडविंस्की

2
कुछ समय पहले मैंने एक वैकल्पिक कार्यान्वयन लिखा था is_base_of: ideone.com/T0C1V यह पुराने GCC संस्करणों के साथ काम नहीं करता है (GCC4.3 ठीक काम करता है)।
जोहान्स शाउब -

3
ठीक है, मैं टहलने जा रहा हूं।
जोकून

2
यह क्रियान्वयन सही नहीं है। is_base_of<Base,Base>::valueहोना चाहिए true; यह रिटर्न false
चेंगिज

जवाबों:


109

यदि वे संबंधित हैं

आइए एक पल के लिए मान लें कि Bवास्तव में इसका आधार है D। फिर कॉल के लिए check, दोनों संस्करण व्यवहार्य हैं क्योंकि Hostइन्हें D* और में परिवर्तित किया जा सकता है B*। यह एक उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम के रूप में द्वारा वर्णित है 13.3.3.1.2से Host<B, D>करने के लिए D*और B*क्रमशः। रूपांतरण कार्यों को खोजने के लिए जो कक्षा को परिवर्तित कर सकते हैं, निम्नलिखित उम्मीदवार कार्यों को पहले checkफ़ंक्शन के अनुसार संश्लेषित किया जाता है13.3.1.5/1

D* (Host<B, D>&)

पहला रूपांतरण फ़ंक्शन एक उम्मीदवार नहीं है, क्योंकि B*इसे परिवर्तित नहीं किया जा सकता है D*

दूसरे फ़ंक्शन के लिए, निम्नलिखित उम्मीदवार मौजूद हैं:

B* (Host<B, D> const&)
D* (Host<B, D>&)

वे दो रूपांतरण फ़ंक्शन उम्मीदवार हैं जो होस्ट ऑब्जेक्ट लेते हैं। पहला इसे कॉन्स्ट रेफरेंस द्वारा लेता है, और दूसरा नहीं। इस प्रकार दूसरा नॉन-कास्ट *thisऑब्जेक्ट ( निहित ऑब्जेक्ट तर्क ) के लिए एक बेहतर मैच है 13.3.3.2/3b1sb4और इसका उपयोग B*दूसरे checkफ़ंक्शन के लिए परिवर्तित करने के लिए किया जाता है ।

यदि आप कांस्ट को हटा देंगे , तो हमारे पास निम्नलिखित उम्मीदवार होंगे

B* (Host<B, D>&)
D* (Host<B, D>&)

इसका मतलब यह होगा कि हम अब कब्ज का चयन नहीं कर सकते। सामान्य अधिभार रिज़ॉल्यूशन परिदृश्य में, कॉल अब अस्पष्ट होगा क्योंकि आमतौर पर रिटर्न प्रकार अधिभार रिज़ॉल्यूशन में भाग नहीं लेंगे। रूपांतरण कार्यों के लिए, हालांकि, एक पिछले दरवाजे है। यदि दो रूपांतरण कार्य समान रूप से अच्छे हैं, तो उनमें से वापसी प्रकार तय करता है कि कौन सबसे अच्छा है 13.3.3/1। इस प्रकार, यदि आप कास्ट हटाएंगे, तो पहले लिया जाएगा, क्योंकि से B*बेहतर रूपांतरित करता है ।B*D*B*

अब क्या उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम बेहतर है? दूसरे या पहले चेक फ़ंक्शन के लिए एक? नियम यह है कि उपयोगकर्ता परिभाषित रूपांतरण अनुक्रमों की तुलना केवल तभी की जा सकती है जब वे उसी रूपांतरण फ़ंक्शन या कंस्ट्रक्टर का उपयोग करते हैं 13.3.3.2/3b2। यह वास्तव में यहाँ है: दोनों दूसरे रूपांतरण फ़ंक्शन का उपयोग करते हैं। ध्यान दें कि इस प्रकार कास्ट महत्वपूर्ण है क्योंकि यह कंपाइलर को दूसरे रूपांतरण फ़ंक्शन को लेने के लिए मजबूर करता है।

चूंकि हम उनकी तुलना कर सकते हैं - कौन सा बेहतर है? नियम यह है कि गंतव्य प्रकार जीत (फिर से 13.3.3.2/3b2) में रूपांतरण फ़ंक्शन के वापसी प्रकार से बेहतर रूपांतरण । इस मामले में, की तुलना में D*बेहतर धर्मान्तरित । इस प्रकार पहले फ़ंक्शन का चयन किया जाता है और हम इनहेरिटेंस को पहचानते हैं!D*B*

ध्यान दें कि जब से हम करने के लिए आवश्यक कभी नहीं वास्तव में एक आधार वर्ग में बदलने का है, हम इस तरह पहचान सकते हैं निजी वंशानुक्रम क्योंकि है कि क्या हम एक से परिवर्तित कर सकते हैं D*एक करने के लिए B*के अनुसार विरासत के रूप पर निर्भर नहीं है4.10/3

यदि वे संबंधित नहीं हैं

अब मान लेते हैं कि वे विरासत से संबंधित नहीं हैं। इस प्रकार पहले कार्य के लिए हमारे पास निम्नलिखित उम्मीदवार हैं

D* (Host<B, D>&) 

और दूसरे के लिए अब हमारे पास एक और सेट है

B* (Host<B, D> const&)

जब से हम नहीं बदल सकते D*करने के लिए B*अगर हम एक विरासत संबंध नहीं मिला है, अब हम दो उपयोगकर्ता परिभाषित रूपांतरण दृश्यों के बीच कोई आम रूपांतरण समारोह है! इस प्रकार, हम अस्पष्ट होंगे यदि इस तथ्य के लिए नहीं कि पहला फ़ंक्शन एक टेम्पलेट है। जब कोई नॉन-टेम्प्लेट फंक्शन होता है तो टेम्प्लेट दूसरी पसंद होते हैं 13.3.3/1। इस प्रकार, हम गैर-टेम्प्लेट फ़ंक्शन का चयन करते हैं (दूसरा एक) और हम पहचानते हैं कि कोई विरासत नहीं है Bऔर D!


2
आह! एंड्रियास के पास पैराग्राफ सही था, बहुत बुरा उन्होंने ऐसा जवाब नहीं दिया :) आपके समय के लिए धन्यवाद, काश मैं इसे पसंदीदा बना सकता।
मैथ्यू एम।

2
यह मेरा पसंदीदा उत्तर होने जा रहा है ... एक प्रश्न: क्या आपने पूरा C ++ मानक पढ़ा है या आप सिर्फ C ++ में काम कर रहे हैं ?? बधाई हो!
मार्को ए।

4
@DavidKernin C ++ कमेटी में काम करने से स्वचालित रूप से आपको पता नहीं चलता है कि C ++ कैसे काम करता है :) इसलिए आपको निश्चित रूप से मानक का हिस्सा पढ़ना होगा जो विवरणों को जानने के लिए आवश्यक है, जो मैंने किया है। यह सब पढ़ा नहीं है, इसलिए मैं निश्चित रूप से मानक पुस्तकालय या सूत्रण संबंधित प्रश्नों के अधिकांश के साथ मदद नहीं कर सकता :)
जोहान्स स्काउब -

1
@underscore_d निष्पक्ष होने के लिए, युक्ति std को मना नहीं करती है :: कुछ संकलक जादू का उपयोग करने का गुण होता है, ताकि मानक पुस्तकालय इम्प्लायर उनका उपयोग कर सकें । वे टेम्पलेट कलाबाजी से बचेंगे जो संकलन समय और मेमोरी उपयोग को गति देने में भी मदद करता है। यह सच है भले ही इंटरफ़ेस कैसा दिखता हो std::is_base_of<...>। यह सब हुड के नीचे है।
जोहान्स शहाब -

2
बेशक, सामान्य पुस्तकालयों boost::को यह सुनिश्चित करने की आवश्यकता है कि उनका उपयोग करने से पहले उनके पास ये आंतरिक उपलब्ध हैं। और मुझे लगता है कि संकलक की मदद के बिना चीजों को लागू करने के लिए उनके बीच एक "चुनौती स्वीकार" मानसिकता का कुछ प्रकार है :)
जोहान्स शबाब -

24

आइए जानें कि चरणों को देखकर यह कैसे काम करता है।

sizeof(check(Host<B,D>(), int()))भाग से शुरू करें । कंपाइलर जल्दी से देख सकता है कि यह check(...)एक फ़ंक्शन कॉल अभिव्यक्ति है, इसलिए इसे ओवरलोड रिज़ॉल्यूशन करने की आवश्यकता है check। वहाँ दो उम्मीदवार अधिभार उपलब्ध हैं, template <typename T> yes check(D*, T);और no check(B*, int);। यदि पहले को चुना जाता है, तो आप प्राप्त करते हैं sizeof(yes), अन्यथाsizeof(no)

अगला, चलो अधिभार संकल्प को देखें। पहला अधिभार एक टेम्पलेट तात्कालिकता है check<int> (D*, T=int)और दूसरा उम्मीदवार है check(B*, int)। प्रदान किए गए वास्तविक तर्क हैं Host<B,D>और int()। दूसरा पैरामीटर स्पष्ट रूप से उन्हें अलग नहीं करता है; यह केवल पहले ओवरलोड को एक खाका बनाने के लिए कार्य करता है। हम बाद में देखेंगे कि टेम्प्लेट भाग प्रासंगिक क्यों है।

अब उन रूपांतरण अनुक्रमों को देखें जिनकी आवश्यकता है। पहले अधिभार के लिए, हमारे पास Host<B,D>::operator D*- एक उपयोगकर्ता-परिभाषित रूपांतरण है। दूसरे के लिए, अधिभार पेचीदा है। हमें एक बी * की आवश्यकता है, लेकिन संभवतः दो रूपांतरण अनुक्रम हैं। एक के माध्यम से है Host<B,D>::operator B*() const। यदि (और केवल अगर) बी और डी विरासत से संबंधित हैं तो रूपांतरण अनुक्रम Host<B,D>::operator D*()+ D*->B*मौजूद होगा। अब मान लीजिए कि D वास्तव में B. से विरासत में मिला है। दो रूपांतरण क्रम हैं Host<B,D> -> Host<B,D> const -> operator B* const -> B*और Host<B,D> -> operator D* -> D* -> B*

तो, संबंधित बी और डी के लिए, no check(<Host<B,D>(), int())अस्पष्ट होगा। नतीजतन, अस्थायी yes check<int>(D*, int)को चुना जाता है। हालाँकि, यदि D, B से विरासत में नहीं मिला है, तो no check(<Host<B,D>(), int())अस्पष्ट नहीं है। इस बिंदु पर, अधिभार संकल्प कम से कम रूपांतरण अनुक्रम के आधार पर नहीं हो सकता है। हालाँकि, समान रूप से रूपांतरण क्रम दिए जाने पर, अधिभार संकल्प गैर-टेम्पलेट फ़ंक्शंस को प्राथमिकता देता है, अर्थात no check(B*, int)

अब आप देखते हैं कि यह कोई फर्क नहीं पड़ता कि विरासत निजी है: यह संबंध केवल no check(Host<B,D>(), int())एक्सेस चेक होने से पहले अधिभार संकल्प से समाप्त करने का कार्य करता है। और आप यह भी देखते हैं कि operator B* constकांस्टेबल क्यों होना चाहिए: और इसके लिए Host<B,D> -> Host<B,D> constकदम की कोई आवश्यकता नहीं है, कोई अस्पष्टता नहीं है, और no check(B*, int)हमेशा चुना जाएगा।


आपकी व्याख्या की उपस्थिति का कोई हिसाब नहीं है const। अगर आपका जवाब सच है तो कोई constजरूरत नहीं है। लेकिन यह सच नहीं है। निकालें constऔर छल से काम नहीं चलेगा।
एलेक्सी मालिस्टोव

कास्ट के बिना दो रूपांतरण अनुक्रम no check(B*, int)अब अस्पष्ट नहीं हैं।
MSalters

यदि आप केवल छोड़ते हैं no check(B*, int), तो संबंधित Bऔर के लिए D, यह अस्पष्ट नहीं होगा। संकलक असंदिग्ध रूप operator D*()से रूपांतरण करने का चयन करेगा , क्योंकि इसमें एक कास्ट नहीं है। यह बल्कि विपरीत दिशा में एक सा है: आप तो हटाने स्थिरांक, आप अस्पष्टता के कुछ समझ में पेश करते हैं, लेकिन जो तथ्य यह है कि द्वारा हल हो गई है operator B*()एक बेहतर वापसी प्रकार जो करने के लिए एक सूचक रूपांतरण की जरूरत नहीं है प्रदान करता है B*की तरह D*है।
जोहान्स शाउब -

यह वास्तव में बिंदु है: अस्थायीता B*से प्राप्त करने के लिए अस्पष्टता दो अलग-अलग रूपांतरण अनुक्रमों के बीच है <Host<B,D>()
MSalters

यह एक बेहतर जवाब है। धन्यवाद! तो, जैसा कि मैंने समझा, यदि एक फ़ंक्शन बेहतर है, लेकिन अस्पष्ट है, तो एक और फ़ंक्शन चुना जाता है?
user1289

4

privateबिट पूरी तरह से द्वारा नजरअंदाज कर दिया है is_base_ofअधिभार संकल्प पहुंच चेकों से पहले होती है क्योंकि।

आप इसे बस सत्यापित कर सकते हैं:

class Foo
{
public:
  void bar(int);
private:
  void bar(double);
};

int main(int argc, char* argv[])
{
  Foo foo;
  double d = 0.3;
  foo.bar(d);       // Compiler error, cannot access private member function
}

यहां भी यही बात लागू होती है, जो Bएक निजी आधार है, चेक को लेने से नहीं रोका जाता है, यह केवल रूपांतरण को रोकता है, लेकिन हम वास्तविक रूपांतरण के लिए कभी नहीं पूछते हैं;)


की तरह। कोई आधार रूपांतरण नहीं किया जाता है। hostको मनमाने ढंग से D*या B*अविकसित अभिव्यक्ति में परिवर्तित किया जाता है । किसी कारण के लिए, कुछ शर्तों के तहत D*बेहतर है B*
पोटाटोस्वाटर

मुझे लगता है कि उत्तर 13.3.1.1.2 में है, लेकिन मुझे विवरणों को छांटना अभी बाकी है :)
एंड्रियास ब्रिंक

मेरा जवाब केवल "क्यों भी निजी काम करता है" भाग की व्याख्या करता है, सेलिबिट्ज़ का जवाब निश्चित रूप से अधिक पूर्ण है, हालांकि मैं मामलों के आधार पर पूर्ण रिज़ॉल्यूशन प्रक्रिया की स्पष्ट व्याख्या का बेसब्री से इंतजार कर रहा हूं।
मैथ्यू एम।

2

यह संभवतः आंशिक आदेश देने के अधिभार संकल्प के साथ कुछ करना है। D * B से अधिक विशिष्ट है यदि D B से व्युत्पन्न है।

सटीक विवरण बल्कि जटिल हैं। आपको विभिन्न अधिभार संकल्प नियमों की प्राथमिकताओं का पता लगाना होगा। आंशिक आदेश एक है। रूपांतरण अनुक्रमों की लंबाई / प्रकार एक और एक है। अंत में, यदि दो व्यवहार्य कार्यों को समान रूप से अच्छा माना जाता है, तो गैर-टेम्पलेट्स को फ़ंक्शन टेम्प्लेट में चुना जाता है।

मुझे यह देखने की ज़रूरत नहीं है कि ये नियम कैसे बातचीत करते हैं। लेकिन ऐसा लगता है कि आंशिक आदेश अन्य अधिभार संकल्प नियमों पर हावी है। जब D B से व्युत्पन्न नहीं होता है तो आंशिक आदेश देने वाले नियम लागू नहीं होते हैं और गैर-टेम्पलेट अधिक आकर्षक होता है। जब डी बी से निकलता है, तो आंशिक ऑर्डरिंग किक करता है और फ़ंक्शन टेम्पलेट को और अधिक आकर्षक बनाता है - जैसा कि ऐसा लगता है।

जैसा कि उत्तराधिकार का भाव होता है: कोड कभी भी डी * से बी * में रूपांतरण की मांग नहीं करता है, जिसके लिए सार्वजनिक विरासत की आवश्यकता होगी।


मुझे लगता है कि यह कुछ ऐसा ही है, मुझे याद है कि इसे लागू करने के is_base_ofलिए योगदानकर्ताओं द्वारा लागू किए गए छोरों और छोरों के बारे में एक व्यापक चर्चा हुई ।
मैथ्यू एम।

The exact details are rather complicated- यही तो बात है। कृपया समझाएँ। मैं जानना चाहता हूं।
एलेक्सी मैलिस्तोव

@ एसेक्स: ठीक है, मैंने सोचा कि मैंने आपको सही दिशा में इशारा किया। इस मामले में विभिन्न ओवरलोड रिज़ॉल्यूशन नियम कैसे बातचीत करते हैं, इसकी जाँच करें। इस ओवरलोडिंग मामले के समाधान के संबंध में B और D से प्राप्त नहीं होने वाले D से प्राप्त होने वाले अंतर के बीच अंतर केवल आंशिक आदेश देने वाला नियम है। अधिभार संकल्प C ++ मानक के of13 में वर्णित है। आप मुफ्त में ड्राफ्ट प्राप्त कर सकते हैं: open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1804.pdf
-10 को बेचिए

ओवरलोड रिज़ॉल्यूशन उस मसौदे में 16 पृष्ठ फैलाता है। मुझे लगता है, अगर आपको वास्तव में नियमों को समझने की आवश्यकता है और इस मामले के लिए उनके बीच की बातचीत को आपको पूरा खंड .313.3 पढ़ना चाहिए। मैं यहां एक उत्तर पाने पर भरोसा नहीं करूंगा जो 100% सही है और आपके मानकों पर निर्भर है।
बिकेगा

यदि आप रुचि रखते हैं, तो कृपया इसके स्पष्टीकरण के लिए मेरा उत्तर देखें।
जोहान्स स्काउब -

0

अपने दूसरे प्रश्न के बाद, ध्यान दें कि यदि यह कांस्ट के लिए नहीं था, तो B == D. के साथ त्वरित रूप से होस्ट बीमार हो जाएगा, लेकिन is_base_of को इस तरह से डिज़ाइन किया गया है कि प्रत्येक वर्ग स्वयं का एक आधार है, इसलिए रूपांतरण ऑपरेटरों में से एक होना चाहिए कांस्टेबल हो

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.