运算符重载和命名空间
可能的重复:
非成员运算符重载应放置在哪里?
浏览时因此,我经常发现涉及重载/定义 std::ostream& 的问题或答案。运算符<<(std::ostream&os, const Foo& foo)或Foo运算符+(const Foo& l, const Foo& r)
。
虽然我知道如何以及何时(不)编写这些运算符,但我对命名空间的事情感到困惑。
如果我有以下类:
namespace bar
{
class Foo {};
}
我应该在哪个命名空间中编写不同的运算符定义?
// 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);
同样的问题也适用于operator+
。那么,这里的良好做法是什么?为什么?
Possible Duplicate:
Where should non-member operator overloads be placed?
While browsing on SO, I often find questions or answer that involves overloading/defining a std::ostream& operator<<(std::ostream& os, const Foo& foo)
or a Foo operator+(const Foo& l, const Foo& r)
.
While I know how and when (not) to write these operators, I'm confused about the namespace
thing.
If I have the following class:
namespace bar
{
class Foo {};
}
In which namespace
should I write the different operator definitions ?
// 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);
The same question applies for the operator+
. So, what is the good practice here and why ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
规则是,在寻找合适的函数重载时,会考虑当前命名空间和参数类型定义的所有命名空间。这称为参数相关查找 (ADL)。
因此,当您有此代码时:
考虑以下命名空间:
因此,您命名的所有三种可能性都将起作用,因此是 '乍一看就足够了。
但是......
您不允许在 ::std 中定义新函数,因此您不能将重载运算符放在该命名空间中。 (您可以在 ::std 中专门化模板,但这不是我们在这里所做的)
其次,“当前名称空间”可能会更改,因此如果您将函数定义放在该名称空间中,可能并不总是能找到它。
所以最后,放置重载运算符的最佳位置是与 Foo 位于同一命名空间中:
The rule is that, when looking for a suitable function overload, both the current namespace and all namespaces of the argument type definitions are considered. This is called Argument Dependent Lookup (ADL).
So when you have this code:
The following namespaces are considered:
So all three possibilities you named, will work and thus are 'good enough' on first glance.
However....
You are not allowed to define new functions in ::std, so you can't put your overloaded operator in that namespace. (You are allowed to specialize templates in ::std, but that's not what we are doing here)
Secondly, the "current namespace" may change, so if you put your function definition in that namespace, it might not always be found.
So in the end, the best place to put the overloaded operator is in the same namespace as Foo:
它应该位于
bar
命名空间中。您必须考虑类的接口由什么组成,并将它们组合在一起。“类描述了一组数据以及对该数据进行操作的函数。”您的自由函数在
Foo
上运行,因此它是Foo
的一部分。它应该与命名空间bar
中的Foo
分组。参数相关查找(或 ADL)将找到该函数。
我们还知道,我们应该更喜欢非友元非成员函数。这意味着,一般来说,您的类将具有其定义和成员函数,紧接着是对该类进行操作的自由函数。
It should be in the
bar
namespace. You must consider what makes up the interface for the class, and group those together."A class describes a set of data along with the functions that operate on that data." Your free function operates on a
Foo
, therefore it is part ofFoo
. It should be grouped withFoo
in the namespacebar
.Argument-dependent lookup, or ADL, will find the function.
We also know that we should prefer non-friend non-member functions. What this means is that, in general, your classes will have their definition and member functions, followed immediately by free functions which operate on the class.
为了使运算符重载正确工作,该函数必须是
与其操作数之一位于同一命名空间中。否则,ADL
没有找到它。这意味着您的类的命名空间
运算符如 + 和 -。理论上,您可以将运算符<<
在 std 或与您的类相同的命名空间中,但是
标准禁止在 std 中定义新函数,所以在这里,你也
将其放在与类相同的命名空间中。
(当然,您通常不会实现 + 或 -,但是 += 和
-=,然后从提供 + 和的模板派生
- 自动地。)
For operator overloading to work correctly, the function must be
in the same namespace as one of its operands. Otherwise, ADL
doesn't find it. This means the namespace of your class for
operators like + and -. Theoretically, you could put operator<<
in either std or the same namespace as your class, but the
standard forbids defining new functions in std, so here too, you
put it in the same namespace as the class.
(And of course, you don't usually implement + or -, but += and
-=, and then derive from a template which provides + and
- automatically.)
最佳选择是选项 1。
为什么?因为当您使用非限定函数名称(重载运算符是函数)时,除了正常名称查找之外,还会应用参数相关查找,即(非正式地)搜索声明参数的所有命名空间。
例如,
运算符也是如此。
另一方面,在选项 2 的情况下,仍然会找到运算符,因为 ostream 位于 std 中(相同的 ADL 规则)。但向 std 命名空间添加内容并不是一个好主意。
从风格上来说,第三个选项很糟糕——如果第一个选项就足够了,为什么还要这样做呢?
所以,绝对是选项 1.HTH
。
The best choice is option 1.
Why? Because when you use a unqualified function name (an an overloaded operator is a function), apart from normal name lookup, Argument-Dependent lookup is applied, that is (informally) all the namespaces where the arguments were declared are searched.
E.g.
The same is with operators.
On the other hand, in case of option 2 the operator still will be found because ostream is in std (same ADL rule). But it is not a good idea to add stuff to std namespace.
And the third option is bad, stylistically - why do it if the first option is sufficient?
So, definitely option 1.
HTH.
好的做法是在与其所属接口的类相同的命名空间中声明(非成员)运算符。
对于像
operator+
这样的东西,这相当简单:它只对 Foo 对象进行操作,因此它应该与 Foo 本身位于相同的命名空间中。对于
operator<<
和operator>>
,您仍然可以在命名空间std
和bar
之间进行选择。首先,您不应该将函数/运算符重载添加到命名空间std
中。其次,这些重载的重要部分不是它们使用流,而是它们读取/写入 Foo 对象。因此将它与 Foo 类捆绑在一起更有意义。还应该注意的是,C++ 规则的设计使得在与其操作的类相同的命名空间中定义的重载运算符几乎总是能被正确找到,而如果声明了运算符,则更容易出错在其他一些不相关的命名空间中。
The good practice is to declare the (non-member) operators in the same namespace as the class whose interface they belong to.
For something like
operator+
, this is rather easy: It operates only on Foo objects, so it should go in the same namespace as Foo itself.For
operator<<
andoperator>>
, you could still chose between namespacesstd
andbar
. First of all, you are not supposed to add function/operator overloads to namespacestd
. And secondly, the important part of these overloads is not that they work with a stream, but that they read/write a Foo object. So it makes more sense to bundle it with the Foo class.It should also be noted that the rules of C++ are designed such that overloaded operators that are defined in the same namespace as the class they operate on, will almost always be correctly found, while this will go wrong much more often if the operators are declared in some other, unrelated, namespace.