Overloading operator<< for a nested private class possible? - c++

How one can overload an operator<< for a nested private class like this one?
class outer {
private:
class nested {
friend ostream& operator<<(ostream& os, const nested& a);
};
// ...
};
When trying outside of outer class compiler complains about privacy:
error: ‘class outer::nested’ is private

You could make the operator<< a friend of outer as well. Or you
could implement it completely inline in nested, e.g.:
class Outer
{
class Inner
{
friend std::ostream&
operator<<( std::ostream& dest, Inner const& obj )
{
obj.print( dest );
return dest;
}
// ...
// don't forget to define print (which needn't be inline)
};
// ...
};

if you want the same thing in two different file (hh, cpp) you have to friend two time the function as follow:
hh:
// file.hh
class Outer
{
class Inner
{
friend std::ostream& operator<<( std::ostream& dest, Inner const& obj );
// ...
};
friend std::ostream& operator<<( std::ostream& dest, Outer::Inner const& obj );
// ...
};
cpp:
// file.cpp:
#include "file.hh"
std::ostream &operator<<( std::ostream& dest, Outer::Inner const& obj )
{
return dest;
}

Related

How to define the friend operator<< for a private class member in a .cpp file, and not in a header?

Compilation of this code fails:
class P {
//public:
class C {
friend std::ostream& operator<<(std::ostream &os, const C &c);
};
};
std::ostream& operator<<(std::ostream &os, const P::C &c) {
return os;
}
error:
test.cpp:12:53: error: 'C' is a private member of 'P'
std::ostream& operator<<(std::ostream &os, const P::C &c) {
^
test.cpp:6:9: note: implicitly declared private here
class C {
^
1 error generated.
Uncommenting public: makes this code to compile. And it obviously can be moved to the class itself.
But what is the correct way to define such operator<< in a cpp file for a private member class?
To see the private elements of P , your operator<< must be friend of P. So in order to be able to access the definition of class C:
class P {
class C {
...
};
friend std::ostream& operator<<(std::ostream &os, const C &c);
};
Then, your current operator will compile. But it can only access public members of C, since it is a friend of the enclosing P but not of the nested C:
std::ostream& operator<<(std::ostream &os, const P::C &c) {
return os;
}
If you also need to access private members of C you need to be double friend:
class P {
class C {
int x; //private
friend std::ostream& operator<<(std::ostream &os, const C &c); // to access private x
};
friend std::ostream& operator<<(std::ostream &os, const C &c); // to access private C
};
std::ostream& operator<<(std::ostream &os, const P::C &c) {
os<<c.x;
return os;
}

C++ namespaces, inner classes and operator resolution

In the C++ namespace myspace I have a class Outer which in turn has an inner class Inner. While I can declare and define a global friend operator QDataStream& operator<<(QDataStream& s, const myspace::Outer& o), I cannot see how to declare a global friend operator QDataStream& operator<<(QDataStream& s, const myspace::Outer::Inner& o). The commented out lines represent a failed attempt. I do not see how to declare the inner class without defining the outer.
namespace myspace {
class Outer;
//class Outer::Inner;
}
QDataStream& operator<<(QDataStream& s, const myspace::Outer& o);
//QDataStream& operator<<(QDataStream& s, const myspace::Outer::Inner& o);
namespace myspace {
class Outer {
friend QDataStream& (::operator <<)(QDataStream&, const Outer&);
class Inner {
//friend QDataStream& (::operator <<)(QDataStream&, const Inner&);
int i;
};
int o;
};
}
I have read
Namespaces and operator resolution, C++ Defining the << operator of an inner class, Accessing private class in operator<< in namespace
and Operator overloading, name resolution and namespaces, but none seem to work.
If I uncomment these lines, the first gives the error message "outer.h:7: error: 'Inner' in 'class myspace::Outer' does not name a type
class Outer::Inner;
^"
This seems to be the key. I cannot declare the inner class.
I am using C++ 11.
This question is not a duplicate of Forward declaration of nested types/classes in C++ if it can can be solved without forward reference.
Given the time lapse, I am posting the correct answer given by Andreas H.
namespace myspace {
class Outer {
class Inner {
friend QDataStream& operator<<(QDataStream&, const Inner&);
int i;
};
friend QDataStream& operator<<(QDataStream&, const Outer&);
friend QDataStream& operator<<(QDataStream&, const Inner&);
int o;
};
QDataStream& operator<<(QDataStream& s, const myspace::Outer& o) {
s << o.o;
return s;
}
QDataStream& operator<<(QDataStream& s, const myspace::Outer::Inner& i) {
s << i.i;
return s;
}
}

Do I really need to bend over backwards for a friend operator<< for a class in a namespace?

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;
}
}

How a friend class can access a private member of a nested class?

Consider the following example:
class SIP{
public:
friend std::ostream& operator<<(std::ostream& os, const SIP& c);
private:
class BusStop;
std::vector<BusStop*> mbusStops;
};
class SIP::BusStop{
private:
struct BusInfo;
std::vector<BusInfo*> mbusStopTerminal;
};
struct SIP::BusStop::BusInfo{
std::string from;
std::string to;
};
std::ostream& operator<<(std::ostream &os, const SIP &c) {
for (std::vector<SIP::BusStop*>::const_iterator it = c.mbusStops.begin();
it != c.mbusStops.end(); it++){
for (std::vector<SIP::BusStop::BusInfo*>::const_iterator it2 = mbusStopTerminal.begin();
it2 != mbusStopTerminal.end(); it2++){
}
}
return os;
}
It won't compile, because the BusInfo struct is private. Friend classes can't access private members of nested classes by default. What should I do in that situation? Is there any workaround?
You could add a stop-printing function to SIP:
class SIP{
public:
friend std::ostream& operator<<(std::ostream& os, const SIP& c);
private:
void printStops(std::ostream& os);
class BusStop;
std::vector<BusStop*> mbusStops;
};
std::ostream& operator<<(std::ostream &os, const SIP &c) {
c.printStops(os);
return os;
}
or you could just add operators all the way down:
class SIP{
public:
friend std::ostream& operator<<(std::ostream& os, const SIP& c);
private:
class BusStop;
std::vector<BusStop*> mbusStops;
};
class SIP::BusStop{
private:
friend std::ostream& operator<<(std::ostream& os, const BusStop& c);
struct BusInfo;
std::vector<BusInfo*> mbusStopTerminal;
};
struct SIP::BusStop::BusInfo{
std::string from;
std::string to;
};
std::ostream& operator<<(std::ostream &os, const SIP::BusStop::BusInfo &i)
{
// Whatever
}
std::ostream& operator<<(std::ostream &os, const SIP::BusStop &c)
{
for (std::vector<SIP::BusStop::BusInfo*>::const_iterator it = mbusStopTerminal.begin();
it != mbusStopTerminal.end(); it++){
os << **it;
}
}
std::ostream& operator<<(std::ostream &os, const SIP &c) {
for (std::vector<SIP::BusStop*>::const_iterator it = c.mbusStops.begin();
it != c.mbusStops.end(); it++){
os << **it;
}
return os;
}
or any combination of approaches that suits your code.

operator<< for nested class

I'm trying to overload the << operator for the nested class ArticleIterator.
// ...
class ArticleContainer {
public:
class ArticleIterator {
// ...
friend ostream& operator<<(ostream& out, const ArticleIterator& artit);
};
// ...
};
If I define operator<< like I usually do, I get a compiler error.
friend ostream& operator<<(ostream& out, const ArticleContainer::ArticleIterator& artit) {
The error is 'friend' used outside of class. How do I fix this?
You don't put the friend keyword when defining the function, only when declaring it.
struct A
{
struct B
{
friend std::ostream& operator<<(std::ostream& os, const B& b);
};
};
std::ostream& operator<<(std::ostream& os, const A::B& b)
{
return os << "b";
}
You must declare it as a friend inside the class, then define it outside the class without the friend keyword.
class ArticleContainer {
public:
class ArticleIterator {
// ...
friend ostream& operator<<(ostream& out, const ArticleIterator& artit);
};
};
// No 'friend' keyword
ostream& operator<<(ostream& out, const ArticleIterator& artit);
the friend keyword is used in the declaration to specify that this func/class is a friend. In the definition outside the class you may not use that keyword. Just remove it