2010-10-08 27 views
8

संभव डुप्लिकेट:
Where should non-member operator overloads be placed?ऑपरेटर ओवरलोडिंग और नामस्थान

जबकि इतने पर ब्राउज़ कर, मैं अक्सर प्रश्न या जवाब है कि अधिक भार शामिल है/एक std::ostream& operator<<(std::ostream& os, const Foo& foo) या एक Foo operator+(const Foo& l, const Foo& r) को परिभाषित करने लगता है।

जबकि मुझे पता है कि इन ऑपरेटरों को लिखने के लिए कैसे और कब (नहीं), मैं namespace चीज़ के बारे में उलझन में हूं।

अगर मैं निम्नलिखित वर्ग है:

namespace bar 
{ 
    class Foo {}; 
} 

जिसमें namespace मैं अलग ऑपरेटर परिभाषाओं लिखना चाहिए?

// Should it be this 

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 

// Or this ? 

namespace std 
{ 
    ostream& operator<<(ostream& os, const bar::Foo& foo); 
} 

// Or this ? 

std::ostream& operator<<(std::ostream& os, const bar::Foo& foo); 

एक ही प्रश्न operator+ के लिए लागू होता है। तो, यहां अच्छा अभ्यास क्या है और क्यों?

+1

की डुप्लीकेट [कहाँ चाहिए गैर-सदस्य ऑपरेटर ओवरलोड को रखा जा सकता है?] (http: // stackoverflow।कॉम/प्रश्न/3623631/अधिभार-ऑपरेटर) –

+0

@ जेम्स मैकनेलिस: मैं वास्तव में एक समान प्रश्न खोजने में विफल रहा। धन्यवाद;) – ereOn

+1

ध्यान रखें कि दूसरा वास्तव में अवैध है, आप केवल 'std' नेमस्पेस में विशेषज्ञता जोड़ सकते हैं। – GManNickG

उत्तर

9

यह bar नामस्थान में होना चाहिए। आपको what makes up the interface for the class पर विचार करना चाहिए, और उनको समूहबद्ध करना चाहिए।

"एक कक्षा उस डेटा पर चलने वाले कार्यों के साथ डेटा के एक सेट का वर्णन करती है।" आपका फ्री फ़ंक्शन Foo पर चलता है, इसलिए यह Foo का हिस्सा है। इसे Foo नामस्थान bar में समूहीकृत किया जाना चाहिए।

Argument-dependent lookup, या एडीएल, समारोह मिलेगा।

हम यह भी जानते हैं कि हमें prefer non-friend non-member functions होना चाहिए। इसका अर्थ यह है कि, सामान्य रूप से, आपके वर्गों में उनकी परिभाषा और सदस्य कार्य होंगे, तुरंत कक्षाओं पर चलने वाले मुक्त कार्यों के बाद उनका पालन किया जाएगा।

+0

समझ में आता है और याद रखना भी आसान है। एक अस्पष्ट कारण के लिए मैंने उन अधिभारों को "मुक्त कार्य" के रूप में नहीं माना है जो वे वास्तव में हैं। स्पष्टीकरण के लिए धन्यवाद। – ereOn

12

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

तो आप इस कोड है जब:

::std::ostream& os = /* something */; 
    const ::bar::Foo& foo = /* something */; 
    os << foo; 

निम्नलिखित नामस्थान माना जाता है:

  • वर्तमान नाम स्थान
  • :: एसटीडी, क्योंकि ओएस 'प्रकार वहाँ परिभाषित किया गया है
  • :: बार, क्योंकि foo का प्रकार परिभाषित किया गया है

तो आपके द्वारा नामित सभी तीन संभावनाएं काम करेंगे और इस तरह पहली नज़र में 'पर्याप्त अच्छी' होंगी।

हालांकि

....

आप में :: एसटीडी नए कार्यों को परिभाषित करने की अनुमति नहीं है, तो आप उस नाम स्थान में अपने अतिभारित ऑपरेटर नहीं डाल सकते हैं।(आपको :: std में टेम्पलेट्स विशेषज्ञ करने की अनुमति है, लेकिन यह हम यहां नहीं कर रहे हैं)

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

तो अंत में, अतिभारित ऑपरेटर डाल करने के लिए सबसे अच्छी जगह फू के रूप में ही नाम स्थान में है:

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 
+0

अच्छा और सटीक। धन्यवाद। मेरा +1 यहाँ है। – ereOn

0

सबसे अच्छा विकल्प विकल्प 1. क्यों है? क्योंकि जब आप एक अयोग्य फ़ंक्शन नाम (एक अधिभारित ऑपरेटर एक फ़ंक्शन है) का उपयोग करते हैं, सामान्य नाम लुकअप के अलावा, तर्क-निर्भर लुकअप लागू होता है, जो कि (अनौपचारिक रूप से) सभी नामस्थान जहां तर्क घोषित किए जाते हैं, की खोज की जाती है। ईजी।

namespace N 
{ 
    class X(){}; 
    void f(X){} 
} 
int main() 
{ 
    N::X x; 
    f(x); //works fine, no need to qualify f like N::f 
} 

ऑपरेटरों के साथ ही यही है।

दूसरी तरफ, विकल्प 2 के मामले में ऑपरेटर अभी भी पाएगा क्योंकि ओस्ट्रीम std (उसी एडीएल नियम) में है। लेकिन स्टडी नेमस्पेस में सामान जोड़ने का अच्छा विचार नहीं है।

और तीसरा विकल्प खराब है, स्टाइलिस्टिक रूप से - ऐसा क्यों होता है यदि पहला विकल्प पर्याप्त है?

तो, निश्चित रूप से विकल्प 1.

एचटीएच।

0

अच्छा अभ्यास उसी नामस्थान में (गैर-सदस्य) ऑपरेटरों को उस वर्ग के रूप में घोषित करना है जिसका इंटरफ़ेस वे हैं।

operator+ जैसे कुछ के लिए, यह आसान है: यह केवल फू ऑब्जेक्ट्स पर चलता है, इसलिए इसे उसी नामस्थान में फू के रूप में जाना चाहिए। operator<< और operator>> के लिए, आप अभी भी नामस्थान std और bar के बीच चुना जा सकता है। सबसे पहले, आपको std नामस्थान पर फ़ंक्शन/ऑपरेटर ओवरलोड जोड़ने की आवश्यकता नहीं है। और दूसरी बात, इन अधिभारों का महत्वपूर्ण हिस्सा यह नहीं है कि वे एक धारा के साथ काम करते हैं, लेकिन वे एक फू ऑब्जेक्ट को पढ़/लिखते हैं। इसलिए इसे फू क्लास के साथ बंडल करने के लिए और अधिक समझदारी होती है।

यह भी ध्यान दिया जाना चाहिए कि सी ++ के नियम इस प्रकार डिज़ाइन किए गए हैं कि ओवरलोडेड ऑपरेटर जो उसी नामस्थान में परिभाषित कक्षा के रूप में परिभाषित होते हैं, लगभग हमेशा सही ढंग से पाए जाते हैं, जबकि यह अधिक बार गलत हो जाएगा ऑपरेटरों को किसी अन्य, असंबंधित, नामस्थान में घोषित किया जाता है।

2

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

(और निश्चित रूप से, आप आमतौर पर लागू नहीं करते + या -, लेकिन + = और - =, और उसके बाद जो प्रदान करता है + और एक टेम्पलेट से निकाले जाते हैं -। स्वचालित रूप से)

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