2015-07-14 6 views
21

मूल रूप से मैं कुछ इस तरह का उपयोग करना चाहते:क्यों (झूठा? ए(): बी())। परीक्षण() केवल तभी संकलित हो जब ए और बी में उप-वर्ग संबंध हो?

(true?a:b).test() 

(true?a.test():b.test()) 

आदेश समय टाइपिंग समारोह एक ही नाम है, तो बचाने के लिए के बजाय

, शुरू में मैंने सोचा कि यह मान्य होना चाहिए, लेकिन मैंने पाया:

#include <stdio.h> 
class A{ 
public: 
    char test(){ 
     return 'A'; 
    } 
}; 

class B{ 
public: 
    char test(){ 
     return 'B'; 
    } 
}; 

int main(){ 
    printf("%c\n",(false?A():B()).test()); 
    return 0; 
} 

संकलन नहीं कर सकते, लेकिन अगर BA के उपवर्ग है:

0,123,
#include <stdio.h> 
class A{ 
public: 
    char test(){ 
     return 'A'; 
    } 
}; 

class B : public A{ 
public: 
    char test(){ 
     return 'B'; 
    } 
}; 

int main(){ 
    printf("%c\n",(false?A():B()).test()); 
    return 0; 
} 

यह संकलित कर सकता है, क्यों?

+0

जिस वस्तु को आप वापस करने का प्रयास करते हैं उसे उसी इंटरफ़ेस के अनुरूप होना चाहिए, आपके मामले में इसे 'test' विधि को लागू करना होगा। कंपाइलर को यह सुनिश्चित करना चाहिए कि किसी भी मामले में वापस ऑब्जेक्ट इंटरफ़ेस के अनुरूप होगा। – mic4ael

+7

क्योंकि सी ++ वास्तव में विधियों के गतिशील प्रेषण नहीं करता है (उदाहरण के लिए पायथन)। –

+5

ध्यान दें कि 'सत्य' a.test(): b.test() 'विफल होगा यदि 'test' परिणामों के लिए कोई सामान्य प्रकार नहीं है। – Jarod42

उत्तर

52

कारण यह है कि (test?a:b) एक अभिव्यक्ति है और इसमें एक प्रकार होना चाहिए। उस प्रकार का सामान्य प्रकार ए और बी है, और असंबंधित प्रकारों में कोई प्रकार सामान्य नहीं है। आधार और व्युत्पन्न वर्ग का सामान्य प्रकार आधार वर्ग है।

ध्यान दें कि प्रश्न में एक धारणा है कि केवल केस जो संकलित करता है जहां एक सामान्य आधार प्रकार है। वास्तव में, यह एक प्रकार से दूसरे में एक स्पष्ट रूपांतरण होने पर भी संकलित करता है।

+1

यदि 'ए' और' बी 'दोनों' सी 'से निकलते हैं, तो परीक्षण करेंगे? ए(): बी() 'काम? –

+0

इस उत्तर के अनुसार, हां (मान लीजिए कि 'परीक्षण' (''' को 'सी' में परिभाषित किया गया है और 'ए' और 'बी' में अधिभारित किया गया है)। '(टेस्ट? ए(): बी()) के बारे में सोचें 'एक प्रकार के लिए उपनाम के रूप में नोटेशन' टाइप Iff (बूल हालत एक्स्प्रेशन, टाइप सच्चे रीसेट, टाइप झूठी रीसेट) {if (conditionExpression) trueResult; और झूठी वापसी वापस; } '। फिर 'आईएफएफ (टेस्ट, ए, बी) .टेस्ट();' अधिक समझ में आता है। – talrnu

+0

@ ब्लूराजा-डैनीफ्लूघोएफ्ट: लॉजिकल जैसा कि ध्वनि हो सकता है, नहीं। मैं सी ++ के डिजाइन और विकास को आसान नहीं बनाता हूं, लेकिन मुझे लगता है कि ऐसा इसलिए है क्योंकि सी ++ में एकाधिक विरासत है। एकल विरासत के साथ, यह विरासत श्रृंखला की 2 जड़ें खोजने और वहां से उस बिंदु पर तुलना करने का एक साधारण मामला है जहां दो श्रृंखलाएं अलग हो जाती हैं। एमआई के साथ, आपको हर रूट के साथ हर रूट की तुलना करना होगा, और अस्पष्टता से निपटना होगा। – MSalters

18

सशर्त ऑपरेटर (?:) ऑपरेटरों के पास एक आम प्रकार होना चाहिए। अर्थात। E1 ? E2 : E3 दिए गए E2 और E3 को स्पष्ट रूप से परिवर्तनीय होना चाहिए। इस प्रकार फिर वापसी प्रकार है जिसे तब पूरी तरह से अभिव्यक्ति के लिए उपयोग किया जाता है।

cppreference से, वे नियम और आवश्यकताओं को सूचीबद्ध करते हैं, लेकिन यहां एक प्रमुख रेखा है जो प्रासंगिक है;

एक सशर्त ऑपरेटर की वापसी प्रकार भी द्विआधारी प्रकार विशेषता std::common_type

कौन सा मूल रूप से कह रही है कि वहाँ एक आम प्रकार होना चाहिए, और std::common_type उस प्रकार की गणना करने के लिए इस्तेमाल किया जा सकता है के रूप में पहुँचा जा सकता है।

अपने कोड स्निपेट (true ? a.test() : b.test()) के आधार पर दोनों a.test() के बाद से काम किया है और b.test()char लौट आए। हालांकि a और b असंबद्ध हैं इस प्रकार स्वयं द्वारा उपयोग नहीं किया जा सकता है।


सी ++ में प्रासंगिक सामग्री (WD n4527) मानक पाया जाता है §5.16 ([expr.cond]) है। लागू होने वाले कई नियम और रूपांतरण हैं, इसका अर्थ यह है कि यदि कोई रूपांतरण नहीं है, या यदि रूपांतरण संदिग्ध है, तो कार्यक्रम खराब हो गया है

4

यदि सशर्त ऑपरेटर के दूसरे और तीसरे ऑपरेंड में "टाइप" नहीं है तो ऑपरेटर में से किसी एक को दूसरे में घुमाने का प्रयास किया जाता है। यदि यह रूपांतरण नहीं बनाया जा सकता है या संदिग्ध है तो कार्यक्रम खराब हो गया है।

यह है क्या कुछ करने के लिए अप्रत्याशित परिणाम लग सकता है, उन दिलचस्प मामलों में से एक capturless lambda with compatible function signature after conversion to a function pointer होगा, उदाहरण के लिए:

#include <iostream> 
#include <algorithm> 
#include <vector> 

int main() { 
    std::vector<int> v(0, 10); 
    bool increase = true; 
    std::sort(v.begin(), v.end(), increase ? 
      [](int lhs, int rhs){return lhs < rhs;} : 
      [](int lhs, int rhs){return lhs > rhs;}); 
    return 0; 
} 

मान्य है। हम निम्नलिखित bug report से देख सकते हैं यह सामान्य रूप से भी लागू होता है और विशेष रूप से इस प्रश्न के लिए सामान्य आधार के बिना कक्षाओं पर लागू होता है। निम्न उदाहरण बग रिपोर्ट से लिया जाता है:

struct A{ typedef void (*F)(); operator F(); }; 
struct B{ typedef void (*F)(); operator F(); }; 

void f() { 
    false ? A() : B(); 
} 

captureless लैम्ब्डा मामले A और B दोनों एक समारोह सूचक में बदला जा सकता है जैसे के बाद से मान्य है।

संदर्भ के लिए खंड 5.16[expr.cond] में मसौदा सी ++ मानक का कहना है:

अन्यथा, यदि दूसरे और तीसरे संकार्य विभिन्न प्रकार है और या तो है (संभवतः सीवी-योग्य) वर्ग टाइप करें, या यदि दोनों एक ही मूल्य श्रेणी के glvalues ​​हैं और सीवी-योग्यता को छोड़कर एक ही प्रकार के हैं, तो उन सभी ऑपरेटरों को दूसरे के प्रकार में बदलने के लिए प्रयास किया जाता है।

और उसके बाद नियमों को शामिल किया गया और फिर कहते हैं:

इस प्रक्रिया का उपयोग करना, यह निर्धारित किया जाता है कि क्या दूसरे संकार्य तीसरे संकार्य मिलान करने के लिए परिवर्तित किया जा सकता है, और चाहे तीसरे संकार्य परिवर्तित किया जा सकता दूसरे ऑपरेंड से मेल खाते हैं। यदि दोनों परिवर्तित हो सकते हैं, या किसी को परिवर्तित किया जा सकता है लेकिन रूपांतरण संदिग्ध है, कार्यक्रम खराब है। यदि न तो परिवर्तित किया जा सकता है, तो ऑपरेटरों को अपरिवर्तित छोड़ दिया जाता है और नीचे वर्णित अनुसार आगे की जांच की जाती है। तो ठीक एक रूपांतरण संभव है, कि रूपांतरण चुना संकार्य लिए आवेदन किया है और परिवर्तित संकार्य इस खंड के शेष के लिए मूल संकार्य के स्थान पर प्रयोग किया जाता है

0

भाषा में जोड़ें:

template<class F> 
struct if_t { 
    bool b; 
    F f; 
    template<class Lhs, class Rhs> 
    auto operator()(Lhs&&lhs, Rhs&&rhs)&& 
    ->std::result_of_t<F(Lhs)> 
    { 
    if (b) return std::forward<F>(f)(std::forward<Lhs>(lhs)); 
    else return std::forward<F>(f)(std::forward<Rhs>(rhs)); 
    } 
    template<class Lhs> 
    void operator()(Lhs&&lhs)&& 
    { 
    if (b) 
     std::forward<F>(f)(std::forward<Lhs>(lhs)); 
    } 
}; 

template<class F> 
if_t<std::decay_t<F>> branch(bool b, F&& f){ 
    return {b,std::forward<F>(f)}; 
} 

तो हम पाते हैं:

branch(false, [&](auto&&arg){return arg.test();}) 
(
    A{}, B{} 
); 

जहां यह तभी काम करता है दोनों A और B एक ०१२३२०२८६७ हैऔर B::test() का वापसी मूल्य A::test() के वापसी मूल्य में परिवर्तित किया जा सकता है।

अफसोस की बात है, A{} और B{} दोनों का निर्माण किया गया है।

template<class T> 
auto make(){return [](auto&&...args){return {decltype(args)(args)...};}} 

branch(false, [&](auto&&arg){return arg().test();}) 
(
    make<A>(), make<B>() 
); 

जो निर्माण को अलग करता है।

सबसे सुरुचिपूर्ण वाक्यविन्यास नहीं है। इसे कुछ साफ किया जा सकता है, लेकिन पर्याप्त नहीं (मेरी राय में)। एक साफ वाक्यविन्यास के साथ सी ++ में आलसी ऑपरेटरों को बनाने का कोई तरीका नहीं है, आप केवल अंतर्निहित लोगों का उपयोग कर सकते हैं।

किसी भी तरह, आपका कोड काम नहीं कर सकता क्योंकि ?: एक अभिव्यक्ति है जो एक प्रकार देता है।ऐसा कोई प्रकार नहीं है जो A और B दोनों का प्रतिनिधित्व कर सके, इसलिए यह काम नहीं कर सकता है। यदि कोई दूसरे का आधार था, या कोई रूपांतरण था, तो यह काम करेगा।

संबंधित मुद्दे