Do I need to go backward for a friend operator << for a class in the namespace?

I want to implement the <<operator to stream my class (let's say it's called Paragraph ). The Paragraph class has some personal data, for this reason I want the (stand-alone) operator <to be a friend. So, I suggest, for example, here on SO . friend , execute operator<< and everything will be fine.

But now I want to put Paragraph inside a namespace, say namespace foo . This is no longer working! If I write:

 namespace foo { class Paragraph { public: explicit Paragraph(std::string const& init) :m_para(init) {} std::string const& to_str() const { return m_para; } private: friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); std::string m_para; }; } // namespace foo 

The compiler tells me that I became friends with foo::operator<< and not ::operator<< . Good, right. So, I will replace the friend line:

  friend std::ostream & ::operator<<(std::ostream &os, const Paragraph& p); 

but I get an error again (from GCC 5.4.0) telling me that ::operator<< not been declared. Ok, let it announce it. Will this work ?:

 namespace foo { std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); class Paragraph { public: explicit Paragraph(std::string const& init) :m_para(init) {} std::string const& to_str() const { return m_para; } private: friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); std::string m_para; }; } // namespace foo 

No, he does not know about the paragraph when reading the declaration ::operator< . Ok, let forward-declare:

 namespace foo { class Paragraph; std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); class Paragraph { /* actual definition here */ } } std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

Bad luck. I get a weird error:

 f.cpp:23:16: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) ^ f.cpp:11:15: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) std::ostream& ::operator<<(std::ostream &os, const foo::Paragraph& p); 

... and here I thought the global namespace and namespace :: is the same ... arent 'they? (sigh). It doesn't matter, release the declaration from the namespace:

 class foo::Paragraph; std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); namespace foo { class Paragraph { /* actual definition here */ } } std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

Still not good enough, now I get the error message "foo" has not been announced. (removes teeth) excellent! Be that way!

 namespace foo { class Paragraph; } std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); namespace foo { class Paragraph { /* actual definition here */ } } std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

so this, but no less, works. This is terrible! Of course, there must be some less sure way to do this ... right?

Note: Assume that operator<< cannot be embedded and must be defined separately.

+7
source share
2 answers

Your problem seems to be related to the fact that we don’t understand how ADL can find the correct operator<< if it is the same namespace as Paragraph . To expand your first example

 // this is what you have already namespace foo { class Paragraph { public: explicit Paragraph(std::string const& init) :m_para(init) {} std::string const& to_str() const { return m_para; } private: friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); std::string m_para; }; } // namespace foo // Now we can add a definition here, or in a different TU namespace foo { std::ostream& operator<<(std::ostream& os, const Paragraph& p) { return os << p.m_para; } } // namespace foo int main() { foo::Paragraph p("hello"); // finds your operator<< using adl std::cout << p << '\n'; } 
+6
source

Just put the definition of the << operator in the namespace along with Paragraph . An argument-dependent search will find it when you inject a Paragraph object into the stream.

 // myheader.h: namespace ns { struct x { /* ... */ friend std::ostream& operator<<(std::ostream&, const x&); }; } // myheader.cpp: #include "myheader.h" namespace ns { std::ostream& operator<<(std::ostream& os, const x& xx) { os << xx.whatever() << '\n'; return os; } } 

Or, if it's small enough to warrant an investment, just do this:

 // myheader.h: namespace ns { struct x { /* ... */ friend std::ostream& operator<<(std::ostream&, const x&); }; inline std::ostream& operator<<(std::ostream& os, const x& xx) { os << xx.whatever() << '\n'; return os; } } 
+3
source

All Articles