I want to implemnent an operator<< for streaming my class (say it's named Paragraph
). Class Paragraph
has some private data, for which reason I want the (freestanding) operator<< function to be a friend. So, I do as suggested, e.g., here on SO. friend
statement, implement the operator<<
and all is well.
But now I want to put Paragraph inside a namespace, say namespace foo
. It no longer works! 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 I've befriended foo::operator<<
, not ::operator<<
. Ok, fair enough. So, I replace the friend line with:
friend std::ostream & ::operator<<(std::ostream &os, const Paragraph& p);
but I get an error again (from GCC 5.4.0), telling me the ::operator<<
has not having been declared. Ok, let's declare it then. 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
Nope, it doesn't know about Paragraph when reading the declaration of ::operator<
. Ok, let's 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 */ }
no luck. I get the 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 was thinking the global namespace and the :: namespace are the same thing... arent' they? (sigh). No matter, let's move the declaration out of 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 "'foo' has not been declared" error. (granshes teeth) fine! 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 than this, works. That's terrible! Surely there must be some kind of less verbose way to do it... right?
Note: Assume the operator<<
can't be inlined and must be defined separately.
It seems your problem stems from not realizing how ADL can find the right operator<<
as long as it is the same namespace as Paragraph
. To expand on 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';
}
Just put the definition of the <<
operator in the namespace along with Paragraph
. Argument-dependent lookup will find it when you insert a Paragraph
object into a 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 inlining, just do that:
// 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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With