ओवरलोड को ठीक से कैसे करें << ओपरेट के लिए ऑपरेटर?


237

मैं मैट्रिक्स संचालन के लिए C ++ में एक छोटी मैट्रिक्स लाइब्रेरी लिख रहा हूं। हालाँकि मेरा कंपाइलर शिकायत करता है, पहले कहाँ नहीं था। इस कोड को 6 महीने के लिए एक शेल्फ पर छोड़ दिया गया था और बीच में मैंने अपने कंप्यूटर को डेबियन एच से लेनी (जी ++ + (डेबियन 4.3.2-1.1) 4.3.2) में अपग्रेड किया था, लेकिन मुझे उबंटू सिस्टम पर समान जी ++ के साथ एक ही समस्या है ।

यहाँ मेरे मैट्रिक्स वर्ग का प्रासंगिक हिस्सा है:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
    }
}

और "कार्यान्वयन":

using namespace Math;

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {

    [...]

}

यह संकलक द्वारा दी गई त्रुटि है:

मैट्रिक्स.cpp: 459: त्रुटि: 'std :: ostream और मठ :: मैट्रिक्स :: ऑपरेटर << (std: ostream और, const गणित :: मैट्रिक्स और)' बिल्कुल एक तर्क लेना चाहिए

मैं इस त्रुटि से थोड़ा भ्रमित हूं, लेकिन फिर मेरे सी ++ ने उन 6 महीनों में बहुत सारे जावा करने के बाद थोड़ा कठोर हो गया है। :-)

जवाबों:


127

आपने अपना फ़ंक्शन घोषित कर दिया है friend। यह वर्ग का सदस्य नहीं है। आपको Matrix::कार्यान्वयन से हटा देना चाहिए । friendइसका मतलब है कि निर्दिष्ट फ़ंक्शन (जो कक्षा का सदस्य नहीं है) निजी सदस्य चर का उपयोग कर सकता है। जिस तरह से आपने फ़ंक्शन को कार्यान्वित किया वह Matrixक्लास के लिए एक इंस्टेंस विधि की तरह है जो गलत है।


7
और आपको इसे मैथ नेमस्पेस (केवल एक नेमस्पेस मैथ के साथ नहीं) के अंदर घोषित करना चाहिए।
डेविड रॉड्रिग्ज - drieas

1
operator<<के नाम स्थान में क्यों होना चाहिए Math? ऐसा लगता है कि यह वैश्विक नामस्थान में होना चाहिए। मैं सहमत हूं कि मेरा कंपाइलर चाहता है कि यह नामस्थान में हो Math, लेकिन इससे मुझे कोई मतलब नहीं है।
मार्क लकाटा

क्षमा करें, लेकिन मैं यह देखने में विफल हूं कि हम यहां मित्र कीवर्ड का उपयोग क्यों करते हैं? जब किसी कक्षा में मित्र संचालक की घोषणा होती है, तो ऐसा लगता है कि हम मैट्रिक्स :: ऑपरेटर << (ostream & os, const Matrix & m) के साथ लागू नहीं कर सकते। इसके बजाय हमें केवल वैश्विक ऑपरेटर ओवरराइड ऑपरेटर का उपयोग करने की आवश्यकता है << ostream & os, const Matrix & m) तो यहां तक ​​कि इसे पहली जगह में वर्ग के अंदर घोषित करने के लिए परेशान क्यों?
पैट्रिक

139

बस आपको एक अन्य संभावना के बारे में बताना: मुझे उसके लिए मित्र परिभाषाओं का उपयोग करना पसंद है:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

फ़ंक्शन को स्वचालित रूप से आसपास के नाम स्थान में लक्षित किया जाएगा Math(भले ही इसकी परिभाषा उस वर्ग के दायरे में दिखाई देती है) लेकिन जब तक आप ऑपरेटर को कॉल नहीं करते तब तक दिखाई नहीं देगा << एक मैट्रिक्स ऑब्जेक्ट के साथ जो तर्क पर निर्भर लुकअप को ऑपरेटर की परिभाषा का पता लगाएगा। यह कभी-कभी अस्पष्ट कॉल के साथ मदद कर सकता है, क्योंकि यह मैट्रिक्स के अलावा तर्क प्रकारों के लिए अदृश्य है। इसकी परिभाषा लिखते समय, आप कुछ संभावित रूप से लंबे उपसर्ग के साथ नाम को अर्हता प्राप्त करने और जैसे टेम्पलेट पैरामीटर प्रदान करने के बिना, सीधे मैट्रिक्स और मैट्रिक्स में परिभाषित नामों का भी उल्लेख कर सकते हैं Math::Matrix<TypeA, N>


77

मेहरदाद जवाब में जोड़ने के लिए,

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

आपके कार्यान्वयन में

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }

4
मुझे समझ नहीं आ रहा है कि यह डाउन वोट क्यों है, यह स्पष्ट करता है कि आप ऑपरेटर को नाम स्थान पर होने की घोषणा कर सकते हैं और एक दोस्त के रूप में भी नहीं और आप संभवतः ऑपरेटर को कैसे घोषित कर सकते हैं।
कल

2
मेहरदाद जवाब में कोड का कोई स्निपेट नहीं था, इसलिए मैंने सिर्फ नामपट्टी में कक्षा के बाहर ले जाकर काम किया।
काल

मैं आपकी बात समझता हूं, मैंने केवल आपके दूसरे स्निपेट को देखा। लेकिन अब मैं देख रहा हूं कि आप ऑपरेटर को क्लास से बाहर ले गए। सलाह के लिये धन्यवाद।
Matthias van der Vlies

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

68

यह मानते हुए कि हम operator <<कक्षा std::ostreamको संभालने के लिए व्युत्पन्न सभी वर्गों के लिए ओवरलोडिंग के बारे में बात कर रहे हैं Matrix(और वर्ग के <<लिए अतिभार नहीं Matrix), हेडर में गणित नामस्थान के बाहर अधिभार फ़ंक्शन को घोषित करने के लिए यह अधिक समझ में आता है।

मित्र फ़ंक्शन का उपयोग केवल तभी करें जब कार्यक्षमता सार्वजनिक इंटरफेस के माध्यम से प्राप्त नहीं की जा सकती है।

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

ध्यान दें कि ऑपरेटर अधिभार नेमस्पेस के बाहर घोषित किया है।

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

दूसरी ओर, यदि आपके अधिभार समारोह को एक दोस्त बनाने की आवश्यकता होती है यानी निजी और संरक्षित सदस्यों तक पहुंच की आवश्यकता होती है।

math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

आपको फ़ंक्शन नाम को सिर्फ एक नाम स्थान ब्लॉक के साथ संलग्न करने की आवश्यकता है using namespace Math;

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}

38

C ++ 14 में आप किसी भी ऑब्जेक्ट को प्रिंट करने के लिए निम्नलिखित टेम्प्लेट का उपयोग कर सकते हैं जिसमें T :: print (std :: ostream &) const है; सदस्य।

template<class T>
auto operator<<(std::ostream& os, T const & t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 

C ++ में 20 अवधारणाओं का उपयोग किया जा सकता है।

template<typename T>
concept Printable = requires(std::ostream& os, T const & t) {
    { t.print(os) };
};

template<Printable T>
std::ostream& operator<<(std::ostream& os, const T& t) { 
    t.print(os); 
    return os; 
} 

दिलचस्प समाधान! एक सवाल - जहां इस ऑपरेटर को वैश्विक दायरे में घोषित किया जाना चाहिए? मुझे लगता है कि इसे सभी प्रकारों के लिए दिखाई देना चाहिए जिसका उपयोग इसे गति देने के लिए किया जा सकता है?
बार्नी

@barney यह उपयोग करने वाली कक्षाओं के साथ आपके अपने नामस्थान में भी हो सकता है।
क्वेंटिनुक

क्या आप अभी वापसी नहीं कर सकते std::ostream&, क्योंकि यह वैसे भी वापसी प्रकार है?
जीन-मिकेल सेलेरिएर

5
@ जीन- MichaëlCelerier घोषणापत्र यह सुनिश्चित करता है कि यह ऑपरेटर केवल तभी उपयोग किया जाता है जब t :: प्रिंट मौजूद हो। अन्यथा यह फ़ंक्शन बॉडी को संकलित करने और संकलन त्रुटि देने का प्रयास करेगा।
क्वेंटिनुक

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