我试图重载<<用于属于用户定义的命名空间的类的运算符。有趣的是,如果我删除所有这些命名空间的东西,程序编译并运行没有问题,但是这个类以某种方式驻留在命名空间中的事实使得文件A.cpp的编译过程失败并出现错误说我试图访问A类中的私有数据(demo.cpp编译正常)。请看一下我的三文件程序和我得到的编译错误:
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;
}
错误:
$ 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;
^
答案 0 :(得分:6)
非合格的好友声明总是引用最小的封闭命名空间的成员。在名称空间usr
中声明类时,该类中的任何非限定友元声明都引用usr
的成员。即你的朋友声明宣称usr::operator <<
为朋友。
在这种情况下,全局::operator <<
仍然是非朋友,这就是当您尝试从usr::A
访问::operator <<
的私人成员时收到错误的原因。
如果你想要这个,你必须
让您的operator <<
成为usr
的成员,或
通过使用限定名称operator <<
确保好友声明明确引用全局::operator <<
(这还需要在尝试之前引入::operator <<
用它的合格名称来引用它。
我会建议第一种方法。如果您的类A
是名称空间usr
的成员,那么最好将所有处理A
的函数声明为usr
的成员。这将帮助您避免许多与参数相关的查找(ADL)问题。
但是如果由于某种原因你需要你的operator <<
仍然是全局命名空间的成员,那么这里是你必须跳过来使它编译的箍(组合成一个翻译单元)
// 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;
}
答案 1 :(得分:0)
在GCC下,你必须通过一些访问修饰符(对const或指针的引用)来分离 return-type 和范围解析运算符(::
token)。
例如,不会在g ++ 7.2.0下编译:
std::string f(int a);
namespace NS
{
class C
{
friend std::string ::f(int a);
// scope operator ^^ is absolutely needed
}
}
但将:
std::string f(int a);
namespace NS
{
class C
{
friend std::string const ::f(int a);
// see const keyword ^
}
}
这将:
std::string f(int a);
namespace NS
{
class C
{
friend std::string& ::f(int a);
// see ref symbol ^
}
}