2013-05-09 6 views
8

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

मेरे पास वीएस 2010 द्वारा लागू सी ++ 11 तक पहुंच है: इसका मतलब है static_assert, enable_if और <type_traits> उपलब्ध हैं।

+0

अब ऐसा करने के बेहतर तरीके हो सकते हैं, लेकिन वीएस -2010 में एक माइक्रोसॉफ्ट विशिष्ट कीवर्ड है जिसे '__interface' कहा जाता है। हालांकि यह एक है) पोर्टेबल नहीं है और बी) आपको किसी कारण से सावधान रहना होगा क्योंकि इन इंटरफेस को वर्चुअल विनाशक होने की अनुमति नहीं है। – Excelcius

+1

यह एक कार्यात्मक आवश्यकता से तकनीकी विनिर्देश की तरह लगता है। आप इसकी आवश्यकता क्यों है? –

+0

@Excelcius हम लिनक्स के साथ ही विंडोज के लिए विकास कर रहे हैं; मैंने वीएस -2010 सूचीबद्ध किया, क्योंकि जीसीसी सी ++ 11 समर्थन में आगे है। – Angew

उत्तर

3

आईएमओ, इस समस्या के लिए कोई स्वच्छ और मंच स्वतंत्र समाधान उपलब्ध नहीं है।

सबसे अच्छा तरीका मैन्युअल रूप से जाने और प्रत्येक विरासत को virtual विरासत में बदलने का सबसे अच्छा तरीका है।
इसे पूरा करने के लिए, अपने इंटरफ़ेस के व्युत्पन्न वर्गों की पहचान करना (class Base कहें) आसान है (!)। नीचे दिए गए चरणों कि के लिए पीछा किया जा सकता है:

  1. final रूप class Base करें (++ सी 11); यानी class Base final { ...
  2. कोड संकलित करें, यह अपने सभी व्युत्पन्न वर्ग के लिए संकलक त्रुटि उत्पन्न होगा
  3. जाओ और हर व्युत्पन्न वर्ग की जाँच करें और के रूप में virtual
  4. विरासत बनाने final कीवर्ड निकालें और कोड सफलतापूर्वक संकलन

जब भी आप ऐसी सैनिटी जांच करना चाहते हैं, तो इस प्रक्रिया (दुर्भाग्यवश) को समय-समय पर पालन करना होगा।

1

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

#include <iostream> 
using namespace std; 

class Hide { 
    struct VInterface { 
     void foo() const { cout << "VInterface::foo()\n"; } 
     VInterface const &as_interface() const { return *this; } 
    protected: 
     virtual ~VInterface() { } 
    }; 
public: 
    struct VBase : virtual VInterface { 
    }; 
}; 
typedef Hide::VBase VBase; 
struct VDiamond1 : VBase { }; 
struct VDiamond2 : VBase { }; 
struct VConcrete : VDiamond1, VDiamond2 { }; 

int main() { 
    VConcrete vc; 
    auto const &vi = vc.as_interface(); 
    vi.foo(); 
} 

ऐसा नहीं है कि विरासत के लिए प्रयोग करने योग्य हो सकता है decltype() और as_interface() का उपयोग करके नाम को फिर से संगठित करने के लिए संभव हो सकता है लेकिन लोगों को मैंने कोशिश की संकलक त्रुटियां हुईं कि नाशक संरक्षित किया गया है, इसलिए मुझे लगता है कि अगर उम्मीद यह संभव है, यह कम से कम अपेक्षाकृत कठिन है और आपकी आवश्यकताओं के लिए पर्याप्त हो सकता है।

3

संकलन समय पर जांचना संभव है। महत्वपूर्ण यह है कि है, अगर हम एक हीरे की पैटर्न:

diamond

आप स्पष्ट रूप A& करने के लिए D& डाली कर सकते हैं। हालांकि, अगर विरासत गैर आभासी है:

not diamond

डाली अस्पष्ट होगा। तो चलो हीरा बनाने की कोशिश करो!

template <typename Base, typename Derived> 
class make_diamond { 
    struct D2 : virtual Base { }; // this one MUST be virtual 
            // otherwise we'd NEVER have a diamond 
public: 
    struct type : Derived, D2 { }; 
}; 

जो पर यह सिर्फ एक और void_t शैली प्रकार विशेषता है इंगित:

template <typename Base, typename Derived, typename = void> 
struct is_virtual_base_of : std::false_type { }; 

template <typename Base, typename Derived> 
struct is_virtual_base_of<Base, Derived, void_t< 
    decltype(static_cast<Base&>(
     std::declval<typename make_diamond<Base, Derived>::type&>())) 
    >> : std::true_type { }; 

तो डाली स्पष्ट है, आंशिक विशेषज्ञता में अभिव्यक्ति मान्य होगा, और कहा कि विशेषज्ञता प्राथमिकता दी जाएगी। यदि कलाकार अस्पष्ट है, तो हमारे पास प्रतिस्थापन विफलता होगी, और प्राथमिक के साथ समाप्त हो जाएगी। ध्यान दें कि Base यहाँ वास्तव में किसी भी virtual सदस्य कार्यों की जरूरत नहीं है:

struct A { }; 
struct B : public A { }; 
struct C : virtual A { }; 

std::cout << is_virtual_base_of<A, B>::value << std::endl; // 0 
std::cout << is_virtual_base_of<A, C>::value << std::endl; // 1 

और अगर यह किसी भी शुद्ध आभासी सदस्य कार्य करता है, हम के बाद से हम कभी नहीं वास्तव में एक वस्तु का निर्माण कर रहे हैं उन्हें ओवरराइड की जरूरत नहीं है ।

struct A2 { virtual void foo() = 0; }; 
struct B2 : public A2 { void foo() override { } }; 
struct C2 : virtual A2 { void foo() override { } }; 

std::cout << is_virtual_base_of<A2, B2>::value << std::endl; // 0 
std::cout << is_virtual_base_of<A2, C2>::value << std::endl; // 1 
बेशक

अगर अपनी कक्षा final चिह्नित है, इस सब पर काम नहीं करेगा। लेकिन फिर, यदि यह final था, तो इससे कोई फर्क नहीं पड़ता कि यह किस तरह की विरासत है।

+0

मुझे यह पता लगाने के लिए सरल दृष्टिकोण पता है कि बेस क्लास वर्चुअल है (उदा। कास्टिंग, पॉइंटर-टू-सदस्य रूपांतरण इत्यादि) लेकिन वे बेस क्लास की पहुंच से प्रभावित होते हैं। अस्पष्टता जांच का उपयोग करने का आपका दृष्टिकोण एक्सेस सुरक्षा से प्रभावित नहीं होता है, इसलिए +1।हालांकि, यह पहले से मौजूद मौजूदा अस्पष्टताओं से प्रभावित है, उदा। 'संरचना डी: बी, सी {}; 'तो' is_virtual_base_of' कुछ है' is_nonfinal_unambiguous_virtual_base_of';) – dyp

+0

@dyp सुनिश्चित नहीं है कि मौजूदा अस्पष्टता से आपका क्या मतलब है? – Barry

+0

'डी डी; ए और ए = डी; 'संदिग्ध है: http://coliru.stacked-crooked.com/a/d92de17312866f0b – dyp

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