Working on a project I did not initiate, I want to add an <<
operator to a class. Problem: the class is a private inner class of an other class, the latter being in a namespace
.
And I cannot make it.
The problem can be simplified this way:
#include <iostream>
#include <map>
namespace A {
class B {
private:
typedef std::map<int, int> C;
C a;
friend std::ostream& operator<<(std::ostream& os, const C &c) {
for (C::const_iterator p = c.begin(); p != c.end(); ++p)
os << (p->first) << "->" << (p->second) << " ";
return os;
}
public:
B() {
a[13] = 10;
std::cout << a << std::endl;
}
};
}
int main() {
A::B c;
}
I try to compile it with g++ test.cpp
: error: no match for ‘operator<<’
. The compiler did not find my overloaded function. I thought it would have been simpler to define it in the header, with no luck. If you think it is more appropriate, I could also define the class in the CPP file, but I do not know how to do.
Last requirement, I cannot use C++11 (unfortunately).
Since the friend operator is first declared inside the class, it's only available by argument-dependent lookup. However, neither of its parameter types are in namespace A
, so it won't be found. C
is an alias for std::map
, so is considered to be in namespace std
for the purposes of ADL.
There are various ways you could fix it, none of which are perfect:
- Declare the function in
namespace A
before the class definition; then it becomes available by normal lookup, not just ADL. However, this breaks the encapsulation somewhat, and might cause problems if anything else tries to overload operator<<
for std::map
.
- Replace the operator overload with a named static (not friend) function, and call it by name.
- Declare
C
as an inner class, rather than an alias for std::map
. This enables ADL without breaking encapsulation, but is a bit awkward if you want it to behave just like std::map
.
Based on Mike Seymour's answer, here's an example for the first solution.
Note operator<<() should be defined outside of class B, and B::C's real type is exposed. It's not perfect but readable...
namespace A {
// It has to expose the B::C's type
std::ostream& operator<<(std::ostream& os, const std::map<int, int> &c);
class B {
private:
typedef std::map<int, int> C;
C a;
friend std::ostream& operator<<(std::ostream& os, const B::C &c);
public:
B() {
a[13] = 10;
std::cout << a << std::endl;
}
};
std::ostream& operator<<(std::ostream& os, const B::C &c) {
for (B::C::const_iterator p = c.begin(); p != c.end(); ++p) {
os << (p->first) << "->" << (p->second) << " ";
}
return os;
}
}