开发者

Operator overloading and namespaces [duplicate]

开发者 https://www.devze.com 2023-01-19 18:44 出处:网络
This question already has answers here: Closed 12 years ago. Possible Duplicate: Where should non-member operator overloads be placed?
This question already has answers here: Closed 12 years ago.

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, cons开发者_JAVA百科t 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 ?


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:

  ::std::ostream& os = /* something */;
  const ::bar::Foo& foo = /* something */;
  os << foo;

The following namespaces are considered:

  • The current namespace
  • ::std, because os' type is defined there
  • ::bar, because foo's type is defined there

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:

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


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 of Foo. It should be grouped with Foo in the namespace bar.

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.


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.)


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.

namespace N
{
   class X(){};
   void f(X){}
}
int main()
{
    N::X x;
    f(x); //works fine, no need to qualify f like N::f
}

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.


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<< and operator>>, you could still chose between namespaces std and bar. First of all, you are not supposed to add function/operator overloads to namespace std. 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消