Reputation:
I'm trying to overload the << operator for a class that's part of a user-defined namespace. What's interesting is that if I remove all that namespace stuff, the program compiles and runs without a problem, but the fact that the class resides in a namespace somehow makes the compilation process for the file A.cpp fail with an error saying that I'm trying to access private data in class A (demo.cpp compiles fine). Please, take a look at my three-file program and the compilation error that I get:
demo.cpp:
#include <iostream>
#include "A.h"
int main() {
usr::A a(4);
std::cout << a << std::endl;
return 0;
}
A.h:
#ifndef A_H_
#define A_H_
#include <iostream>
namespace usr {
class A {
private:
int m_x;
public:
A(int x);
friend std::ostream& operator<<(std::ostream& os, const usr::A& a);
};
}
#endif // A_H_
A.cpp:
#include "A.h"
usr::A::A(int x) : m_x(x) {}
std::ostream& operator<<(std::ostream& os, const usr::A& a) {
os << a.m_x;
return os;
}
Error:
$ g++ -c A.cpp
In file included from A.cpp:1:0:
A.h: In function ‘std::ostream& operator<<(std::ostream&, const usr::A&)’:
A.h:10:17: error: ‘int usr::A::m_x’ is private
int m_x;
^
A.cpp:7:13: error: within this context
os << a.m_x;
^
Upvotes: 6
Views: 4017
Reputation: 721
Under GCC you have to separate return-type and scope resolution operator (::
token) via some access modifier (reference on const or pointer).
For example, this will not compile under g++7.2.0:
std::string f(int a);
namespace NS
{
class C
{
friend std::string ::f(int a);
// scope operator ^^ is absolutely needed
}
}
But this will:
std::string f(int a);
namespace NS
{
class C
{
friend std::string const ::f(int a);
// see const keyword ^
}
}
And this will:
std::string f(int a);
namespace NS
{
class C
{
friend std::string& ::f(int a);
// see ref symbol ^
}
}
Upvotes: 0
Reputation: 320361
Non-qualified friend declarations always refer to members of the smallest enclosing namespace. When a class is declared in namespace usr
, any non-qualified friend declaration inside that class refers to members of usr
. I.e. your friend declaration declared usr::operator <<
as a friend.
The global ::operator <<
remains a non-friend in this case, which is why you get the error when you attempt to access private members of usr::A
from ::operator <<
.
If you want this to work, you have to either
Make your operator <<
a member of usr
, or
Make sure the friend declaration explicitly refers to global operator <<
by using a qualified name ::operator <<
(this will also require introducing ::operator <<
before trying to refer to it by its qualified name).
I would suggest the first approach. If your class A
is a member of namespace usr
, then it is a good idea to declare all functions that handle A
as members of usr
as well. This will help you to avoid many problems with argument-dependent lookup (ADL) down the road.
But if for some reason you need your operator <<
to remain a member of global namespace, then here are the hoops you'd have to jump through to make it compile (combined into a single translation unit)
// Pre-declare `usr::A` to enable pre-declaration of our `::operator <<`
namespace usr {
class A;
}
// Pre-declare our `::operator <<`
std::ostream& operator<<(std::ostream& os, const usr::A& a);
namespace usr {
class A {
private:
int m_x;
public:
A(int x);
friend std::ostream& ::operator<<(std::ostream& os, const usr::A& a);
// Friend declaration uses qualified name - it explicitly
// refers to `::operator <<` declared above
};
}
usr::A::A(int x) : m_x(x) {}
std::ostream& operator<<(std::ostream& os, const usr::A& a) {
os << a.m_x;
return os;
}
int main() {
usr::A a(4);
std::cout << a << std::endl;
}
Upvotes: 10