2011-03-14 9 views
10

सी ++ पुस्तकालयों के लोड, मानक शामिल, आपको पुस्तकालयों में उपयोग के लिए अपनी वस्तुओं को अनुकूलित करने की अनुमति देता है। पसंद अक्सर एक सदस्य समारोह या एक ही नामस्थान में एक मुक्त समारोह के बीच होता है।मुक्त कार्यों या सदस्य कार्यों के माध्यम से विस्तार के यांत्रिकी

मैं मैकेनिक्स जानना चाहता हूं और कॉल को प्रेषित करने के लिए लाइब्रेरी कोड का उपयोग करता हूं जो इन "एक्सटेंशन" कार्यों में से एक को कॉल करेगा, मुझे पता है कि यह निर्णय संकलन समय के दौरान होना चाहिए और टेम्पलेट्स शामिल करना होगा। निम्नलिखित रनटाइम psuedocode संभव/गैर-अर्थ नहीं है, कारण इस प्रश्न के दायरे से बाहर हैं।

if Class A has member function with signature FunctionSignature 
    choose &A.functionSignature(...) 
else if NamespaceOfClassA has free function freeFunctionSignature 
    choose freeFunctionSignature(...) 
else 
    throw "no valid extension function was provided" 

उपरोक्त कोड रनटाइम कोड की तरह दिखता है: /। तो, लाइब्रेरी एक नामस्थान नामस्थान को कैसे समझती है, यह तीन स्थितियों का पता कैसे लगाती है, इससे बचने की ज़रूरत वाले अन्य नुकसान क्या हैं।

मेरे प्रश्न के लिए प्रेरणा मेरे लिए पुस्तकालयों में प्रेषण ब्लॉक खोजने में सक्षम होना है, और अपने कोड में संरचनाओं का उपयोग करने में सक्षम होना है। तो, विस्तृत उत्तरों मदद मिलेगी।

!! जीतने के लिए !!

ठीक है तो स्टीव (और टिप्पणियों) एडीएल और एसएफआईएनएई के उत्तर के अनुसार संकलन समय पर प्रेषण को तारों के तारों के लिए महत्वपूर्ण संरचनाएं हैं। मुझे अपना सिर एडीएल (प्राइमेटिवली) और एसएफआईएनएई (फिर से रूढ़िवादी) के आसपास मिला है। लेकिन मुझे नहीं पता कि वे किस तरह से सोचते हैं कि वे कैसे सोचते हैं।

मैं एक उदाहरण उदाहरण देखना चाहता हूं कि इन दो संरचनाओं को एक साथ कैसे रखा जा सकता है ताकि पुस्तकालय संकलित समय पर चयन कर सके कि किसी ऑब्जेक्ट में उपयोगकर्ता द्वारा समर्थित सदस्य फ़ंक्शन को कॉल किया जाए या उपयोगकर्ता द्वारा प्रदान की गई निःशुल्क फ़ंक्शन प्रदान की गई हो एक ही ऑब्जेक्ट का नामस्थान। यह केवल उपरोक्त दो संरचनाओं का उपयोग करके किया जाना चाहिए, किसी भी प्रकार का रनटाइम प्रेषण नहीं होना चाहिए।

आइए प्रश्न में ऑब्जेक्ट को NS::Car कहा जाता है, और इस ऑब्जेक्ट को MoveForward(int units) का व्यवहार सदस्य कार्य के रूप में प्रदान करने की आवश्यकता है। यदि ऑब्जेक्ट के नामस्थान से व्यवहार को उठाया जाना है तो यह शायद MoveForward(const Car & car_, int units) जैसा दिखेगा। आइए उस फ़ंक्शन को परिभाषित करें जो mover(NS::direction d, const NS::vehicle & v_) प्रेषण करना चाहता है, जहां दिशा एक enum है, और v_ NS::car का आधार वर्ग है।

+0

आप स्ट्रीम में आउटपुट करने के लिए अपनी कक्षा में 'ऑपरेटर <<' ओवरराइड नहीं कर सकते हैं। एक सदस्य ऑपरेटर को ऑपरेटर के बाईं ओर कक्षा होना चाहिए, दाईं ओर नहीं। इसके अलावा, नाम लुकअप कंपाइलर का हिस्सा है, लाइब्रेरी नहीं, और टेम्पलेट क्लास या टेम्पलेट फ़ंक्शन शामिल होने पर केवल टेम्पलेट्स को शामिल करेगा। यदि आप पूछ रहे हैं कि नाम लुकअप कैसे किया जाता है, तो कृपया अपने प्रश्न को स्पष्ट करें। अन्यथा, मुझे नहीं पता कि आप क्या पूछ रहे हैं। –

+0

@ डेविड हाँ, मैं 'ऑपरेटर के बारे में गलत था <<' मैंने उदाहरण हटा दिया, लेकिन कोई मेरा प्रश्न संपादित कर रहा था और हटाने को ओवरराइट कर रहा था: डी –

उत्तर

2

ठीक है, मैं आपको बता सकता हूं कि संकलन समय पर किसी निश्चित नाम (और हस्ताक्षर) के सदस्य कार्यों की उपस्थिति का पता कैसे लगाया जाए।मेरा एक दोस्त यहाँ यह बताता है:

Detecting the Existence of Member Functions at Compile-Time

हालांकि कि आप कहां जाना चाहते हैं, नहीं मिलेगा क्योंकि यह केवल स्थिर प्रकार के लिए काम करता है। चूंकि आप "संदर्भ-से-वाहन" पास करना चाहते हैं, इसलिए परीक्षण करने का कोई तरीका नहीं है कि गतिशील प्रकार (संदर्भ के पीछे ठोस वस्तु का प्रकार) में ऐसे सदस्य फ़ंक्शन हैं या नहीं।

यदि आप स्थिर प्रकार के लिए व्यवस्थित हैं, तो एक बहुत ही समान काम करने का एक और तरीका है। यह लागू करता है "यदि उपयोगकर्ता ओवरलोडेड फ्री फ़ंक्शन प्रदान करता है, तो इसे कॉल करें, अन्यथा सदस्य फ़ंक्शन को कॉल करने का प्रयास करें"। और यह इस प्रकार है:

namespace your_ns { 

template <class T> 
void your_function(T const& t) 
{ 
    the_operation(t); // unqualified call to free function 
} 

// in the same namespace, you provide the "default" 
// for the_operation as a template, and have it call the member function: 

template <class T> 
void the_operation(T const& t) 
{ 
    t.the_operation(); 
} 

} // namespace your_ns 

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

उदाहरण:

namespace users_ns { 

class foo {}; 

void the_operation(foo const& f) 
{ 
    std::cout << "foo\n"; 
} 

template <class T> 
class bar {}; 

template <class T> 
void the_operation(bar<T> const& b) 
{ 
    std::cout << "bar\n"; 
} 

} // namespace users_ns 

संपादित करें: स्टीव जेसप के जवाब फिर से पढ़ने के बाद, मुझे लगता है कि मूल रूप से वह क्या लिखा है, केवल अधिक शब्दों :)

मुक्त कार्यों
+0

कोड में अवधारणा के अधिक शब्द, और स्पष्ट प्रदर्शन। उस – Novelocrat

+0

के लिए +1 धन्यवाद दोस्त, मैं वास्तव में स्थिर प्रकार के मामले में रूचि रखता था। लिखे गए कोड को देखते हुए "आह" प्रभाव मिलता है: डी –

8

लाइब्रेरी रनटाइम पर ऐसा नहीं करता है, कॉलिंग कोड संकलित होने पर कंपाइलर द्वारा प्रेषण किया जाता है। तर्कों में से एक के रूप में एक ही नामस्थान में नि: शुल्क कार्य "तर्क-निर्भर लुकअप" (एडीएल) नामक एक तंत्र के नियमों के अनुसार पाए जाते हैं, जिन्हें कभी-कभी "कोएनिग लुकअप" कहा जाता है।

ऐसे मामलों में जहां आपके पास एक मुफ्त फ़ंक्शन या सदस्य फ़ंक्शन को लागू करने का विकल्प है, ऐसा इसलिए हो सकता है क्योंकि लाइब्रेरी एक फ़ंक्शन प्रदान करता है जो सदस्य फ़ंक्शन को कॉल करता है। फिर यदि आपका ऑब्जेक्ट एडीएल द्वारा समान नाम का एक फ़ंक्शन प्रदान करता है, तो यह टेम्पलेट को तुरंत चालू करने से बेहतर मिलान होगा, और इसलिए पहले चुना जाएगा। जैसा कि Space_C0wb0y कहता है, वे टेम्पलेट में सदस्य फ़ंक्शन का पता लगाने के लिए SFINAE का उपयोग कर सकते हैं, और यह मौजूद है या नहीं, इसके अनुसार कुछ अलग करें।

पर सदस्य फ़ंक्शन जोड़कर आप std::cout << x; के व्यवहार को नहीं बदल सकते हैं, इसलिए मुझे पूरा यकीन नहीं है कि आपका क्या मतलब है।

+0

प्रेषण के लिए महत्वपूर्ण अन्य तंत्र [SFINAE] है (http: //en.wikipedia .org/wiki/Substitution_failure_is_not_an_error)। –

+0

पॉइंटर्स के लिए धन्यवाद, इससे मुझे चीज़ों को समझने में मदद करनी चाहिए। इसके अलावा, आप स्ट्रीम अर्थशास्त्र के बारे में सही थे, और मैंने उदाहरण हटा दिया है। आखिरी ऐसे कोड को मैंने देखा है :: serialization, जो आपको दोनों प्रकार के कार्यों को प्रदान करने की अनुमति देता है। –

0

Altought, कभी कभी, डेवलपर्स के लिए इस्तेमाल किया जा सकता है या के साथ है वर्ग कार्यों, एक दूसरे के उपयोग के लिए, कुछ स्थितियां हैं।

(1) वस्तु/क्लास कार्य ("विधि), पसंद कर रहे हैं, जब इसके purpouse के सबसे केवल वस्तु को प्रभावित, या वस्तुओं अन्य वस्तुओं की रचना करने inteded कर रहे हैं।

// object method 
MyListObject.add(MyItemObject); 
MyListObject.add(MyItemObject); 
MyListObject.add(MyItemObject); 

(2) नि: शुल्क (" वैश्विक "या" मॉड्यूल ") कार्यों पसंद कर रहे हैं, जब कई वस्तुओं शामिल है, और वस्तुओं हिस्सा/आदिम प्रकार एक दूसरे को। या, जब समारोह के तरीकों के बिना सादे डेटा (structs का उपयोग करता है, से बना) नहीं हैं।

MyStringNamespace.MyStringClass A = new MyStringNamespace.MyStringClass("Mercury"); 
MyStringNamespace.MyStringClass B = new MyStringNamespace.MyStringClass("Jupiter"); 
// free function 
bool X = MyStringNamespace.AreEqual(A, B); 

जब कुछ सामान्य मॉड्यूल एक्सेस ऑब्जेक्ट्स को एक्सेस करते हैं, तो सी ++ में, आपके पास वें है ई "दोस्त कीवर्ड" जो उन्हें स्कोप के बिना, ऑब्जेक्ट विधियों तक पहुंचने की अनुमति देता है।

class MyStringClass { 
    private: 
    // ... 
    protected: 
    // ... 
    // not a method, but declared, to allow access 
    friend: 
    bool AreEqual(MyStringClass A, MyStringClass B); 
} 

bool AreEqual(MyStringClass A, MyStringClass B) { ... } 

"लगभग शुद्ध वस्तु उन्मुख" जावा या सी #, जहाँ आप मुफ्त कार्यों नहीं हो सकता है की तरह प्रोग्रामिंग भाषाओं में नि: शुल्क कार्यों स्थिर तरीकों, जो सामान और अधिक जटिल बना देता है के साथ बदल रहे हैं।

1

तुम सिर्फ एक ठोस उदाहरण के लिए देख रहे हैं, पर विचार करें:

#include <cassert> 
#include <type_traits> 
#include <iostream> 

namespace NS 
{ 
    enum direction { forward, backward, left, right }; 

    struct vehicle { virtual ~vehicle() { } }; 

    struct Car : vehicle 
    { 
     void MoveForward(int units) // (1) 
     { 
      std::cout << "in NS::Car::MoveForward(int)\n"; 
     } 
    }; 

    void MoveForward(Car& car_, int units) 
    { 
     std::cout << "in NS::MoveForward(Car&, int)\n"; 
    } 
} 

template<typename V> 
class HasMoveForwardMember // (2) 
{ 
    template<typename U, void(U::*)(int) = &U::MoveForward> 
    struct sfinae_impl { }; 

    typedef char true_t; 
    struct false_t { true_t f[2]; }; 

    static V* make(); 

    template<typename U> 
    static true_t check(U*, sfinae_impl<U>* = 0); 
    static false_t check(...); 

public: 
    static bool const value = sizeof(check(make())) == sizeof(true_t); 
}; 

template<typename V, bool HasMember = HasMoveForwardMember<V>::value> 
struct MoveForwardDispatcher // (3) 
{ 
    static void MoveForward(V& v_, int units) { v_.MoveForward(units); } 
}; 

template<typename V> 
struct MoveForwardDispatcher<V, false> // (3) 
{ 
    static void MoveForward(V& v_, int units) { NS::MoveForward(v_, units); } 
}; 

template<typename V> 
typename std::enable_if<std::is_base_of<NS::vehicle, V>::value>::type // (4) 
mover(NS::direction d, V& v_) 
{ 
    switch (d) 
    { 
    case NS::forward: 
     MoveForwardDispatcher<V>::MoveForward(v_, 1); // (5) 
     break; 
    case NS::backward: 
     // ... 
     break; 
    case NS::left: 
     // ... 
     break; 
    case NS::right: 
     // ... 
     break; 
    default: 
     assert(false); 
    } 
} 

struct NonVehicleWithMoveForward { void MoveForward(int) { } }; // (6) 

int main() 
{ 
    NS::Car v; // (7) 
    //NonVehicleWithMoveForward v; // (8) 
    mover(NS::forward, v); 
} 

HasMoveForwardMember(2) एक metafunction कि हस्ताक्षर के साथ उस नाम के एक सदस्य समारोह के अस्तित्व के लिए जाँच करता है किसी दिए गए वर्ग V में void(V::*)(int)MoveForwardDispatcher(3) सदस्य फ़ंक्शन को कॉल करने के लिए इस जानकारी का उपयोग करता है यदि यह मौजूद है या यदि कोई फ़ंक्शन नहीं है तो उसे कॉल करने के लिए वापस आ जाता है। mover बस MoveForward से MoveForwardDispatcher(5) का आविष्कार प्रस्तुत करता है।

कोड के रूप में पोस्ट लागू करेगा Car::MoveForward(1), लेकिन अगर यह सदस्य समारोह निकाल दिया जाता है, नाम, या उसके हस्ताक्षर बदल गया है, NS::MoveForward बजाय बुलाया जाएगा।

भी ध्यान रखें कि क्योंकि mover एक टेम्पलेट है, एक SFINAE जांच जगह NS::vehicle से ली गई वस्तुओं केवल अनुमति देने का अर्थ विज्ञान बनाए रखने के लिए में डाल दिया जाना चाहिए के लिए v_(4) में पारित किया जाना है। प्रदर्शित करने के लिए, अगर एक बाहर टिप्पणी (7) और uncomments (8), mover प्रकार NonVehicleWithMoveForward(6) की एक वस्तु है, जो हम तथ्य यह है कि HasMoveForwardMember<NonVehicleWithMoveForward>::value == true के बावजूद अस्वीकृत करने के लिए चाहते हैं के साथ बुलाया जाएगा।

(नोट:। अपने मानक पुस्तकालय std::enable_if और std::is_base_of साथ नहीं आता है, तो का उपयोग std::tr1:: या boost:: उपलब्ध के रूप में के बजाय वेरिएंट)

तरह से कोड की इस तरह आम तौर पर प्रयोग किया जाता है हमेशा कॉल करने के लिए है नि: शुल्क फ़ंक्शन, और MoveForwardDispatcher जैसे कुछ के मामले में नि: शुल्क फ़ंक्शन को कार्यान्वित करें, जैसे कि फ्री फ़ंक्शन बस उस ऑब्जेक्ट के सदस्य फ़ंक्शन में पारित होने पर कॉल करता है, बिना किसी संभावित सदस्य के प्रत्येक संभावित प्रकार के उस फ्री फ़ंक्शन के ओवरलोड को लिखने के बिना समारोह।

0

यदि मुझे सही ढंग से समझ में आया तो आपकी समस्या को आसानी से (शायद एकाधिक) विरासत का उपयोग करके हल किया जा सकता है।

namespace NS { 
void DoSomething() 
{ 
    std::cout << "NS::DoSomething()" << std::endl; 
} 
} // namespace NS 

एक आधार वर्ग है जो एक ही समारोह अग्रेषित करता है का उपयोग करें:: आप कहीं एक namespace मुक्त कार्य हो

struct SomethingBase 
{ 
    void DoSomething() 
    { 
     return NS::DoSomething(); 
    } 
}; 

तो कुछ वर्ग SomethingBase से एक पाने DoSomething (लागू नहीं करता है) बुला यह SomethingBase कॉल करेंगे :: DoSomething() -> एन एस :: DoSomething():

struct A : public SomethingBase // probably other bases 
{ 
    void DoSomethingElse() 
    { 
     std::cout << "A::DoSomethingElse()" << std::endl; 
    } 
}; 

अन्य वर्ग बी SomethingBase से पाने DoSomething() कॉल यह बी :: DoSomething() फोन करेगा लागू करते हैं तो:

struct B : public SomethingBase // probably other bases 

{ 
    void DoSomething() 
    { 
     std::cout << "B::DoSomething()" << std::endl; 
    } 
}; 

तो एक वस्तु SomethingBase से पाने सदस्य निष्पादित करेंगे मौजूदा, या मुक्त समारोह अन्यथा पर DoSomething() कॉल। ध्यान दें कि फेंकने के लिए कुछ भी नहीं है, अगर आपके कॉल में कोई मिलान नहीं है तो आपको संकलन त्रुटि मिलती है।

int main() 
{ 
    A a; 
    B b; 
    a.DoSomething(); // "NS::DoSomething()" 
    b.DoSomething(); // "B::DoSomething()" 
    a.DoSomethingElse(); // "A::DoSomethingElse()" 
    b.DoSomethingElse(); // error 'DoSomethingElse' : is not a member of 'B' 
} 
संबंधित मुद्दे