我真的需要为命名空间中的类的朋友运算符<< 向后弯腰吗?
Posted
技术标签:
【中文标题】我真的需要为命名空间中的类的朋友运算符<< 向后弯腰吗?【英文标题】:Do I really need to bend over backwards for a friend operator<< for a class in a namespace? 【发布时间】:2017-03-14 22:43:26 【问题描述】:我想实现一个运算符Paragraph)。 Paragraph
类有一些私有数据,因此我希望(独立)运算符here on SO。 friend
声明,执行operator<<
一切都很好。
但现在我想将 Paragraph 放在命名空间中,例如 namespace foo
。它不再起作用了!如果我写:
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
编译器告诉我,我结识了foo::operator<<
,而不是::operator<<
。好,可以。因此,我将朋友行替换为:
friend std::ostream & ::operator<<(std::ostream &os, const Paragraph& p);
但我再次收到错误消息(来自 GCC 5.4.0),告诉我 ::operator<<
尚未声明。好的,那我们就声明吧。这行得通吗?:
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
不,它在阅读::operator<
的声明时不知道Paragraph。好的,让我们向前声明:
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 */
运气不好。我收到奇怪的错误:
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);
...在这里我在想全局命名空间和 :: 命名空间是一回事...不是吗? (叹)。没关系,让我们将声明移出命名空间:
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 */
仍然不够好,现在我收到“'foo' has not been declared”错误。 (咬牙)好!就这样吧!
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 */
所以这个,但不少于这个,有效。这可怕!当然必须有某种不那么冗长的方式来做到这一点......对吧?
注意:假设operator<<
不能内联,必须单独定义。
【问题讨论】:
【参考方案1】:您的问题似乎源于没有意识到ADL 可以找到正确的operator<<
,只要它与Paragraph
相同的命名空间。扩展您的第一个示例
// 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';
【讨论】:
你的第一句话一针见血。或者我应该说我的头。 @einpoklum 此外,这是保证名称查找成功的唯一解决方案。::operator<<
在某些情况下找不到,因为它不是 ADL 查找集的一部分。【参考方案2】:
只需将<<
运算符的定义与Paragraph
一起放在命名空间中即可。当您将 Paragraph
对象插入流中时,依赖于参数的查找将找到它。
// 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;
或者,如果它足够小以保证内联,就这样做:
// 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;
【讨论】:
以上是关于我真的需要为命名空间中的类的朋友运算符<< 向后弯腰吗?的主要内容,如果未能解决你的问题,请参考以下文章