运算符重载和命名空间

ere*_*eOn 12 c++ namespaces operator-overloading operator-keyword

可能重复:
非成员运算符重载应放在何处?

在浏览SO时,我经常会发现涉及重载/定义a std::ostream& operator<<(std::ostream& os, const Foo& foo)或a的问题或答案Foo operator+(const Foo& l, const Foo& r).

虽然我知道如何以及何时(不)编写这些操作符,但我对namespace此事感到困惑.

如果我有以下课程:

namespace bar
{
  class Foo {};
}
Run Code Online (Sandbox Code Playgroud)

namespace我应该在哪个中编写不同的运算符定义?

// Should it be this

namespace bar
{
  std::ostream& operator<<(std::ostream& os, const Foo& foo);
}

// Or this ?

namespace std
{
  ostream& operator<<(ostream& os, const bar::Foo& foo);
}

// Or this ?

std::ostream& operator<<(std::ostream& os, const bar::Foo& foo);
Run Code Online (Sandbox Code Playgroud)

同样的问题适用于operator+.那么,这里的好习惯是什么?为什么

Sjo*_*erd 14

规则是,在查找合适的函数重载时,会考虑当前名称空间和参数类型定义的所有名称空间.这称为参数依赖查找(ADL).

所以当你有这个代码时:

  ::std::ostream& os = /* something */;
  const ::bar::Foo& foo = /* something */;
  os << foo;
Run Code Online (Sandbox Code Playgroud)

考虑以下名称空间:

  • 当前的命名空间
  • :: std,因为在那里定义了os'类型
  • :: bar,因为那里定义了foo的类型

因此,您命名的所有三种可能性都将起作用,因此乍一看"足够好".

然而....

您不能在:: std中定义新函数,因此您不能将重载的运算符放在该命名空间中.(你可以在:: std中专门化模板,但这不是我们在这里做的)

其次,"当前命名空间"可能会发生变化,因此如果将函数定义放在该命名空间中,则可能无法始终找到它.

所以最后,放置重载运算符的最佳位置与Foo位于同一个命名空间中:

namespace bar   
{   
  std::ostream& operator<<(std::ostream& os, const Foo& foo);   
}   
Run Code Online (Sandbox Code Playgroud)


GMa*_*ckG 12

它应该在bar命名空间中.您必须考虑构成该类接口的内容,并将它们组合在一起.

"一个类描述了一组数据以及对该数据进行操作的函数." 你的免费功能在a上运行Foo,因此它是其中的一部分Foo.它应该Foo在命名空间中分组bar.

依赖于参数的查找或ADL将找到该函数.

我们也知道我们应该更喜欢非朋友的非会员功能.这意味着,通常,您的类将具有其定义和成员函数,紧接着是在类上运行的自由函数.