views:

93

answers:

2

Since I need to add an operator& for the std::tr1::array<bool, N> I wrote the following lines

template<std::size_t N>
std::tr1::array<bool, N>
operator& (const std::tr1::array<bool, N>& a,
           const std::tr1::array<bool, N>& b)
{
  std::tr1::array<bool, N> result;
  std::transform(a.begin(), a.end(), b.begin(), result.begin(),
                 std::logical_and<bool>());
  return result;
}

Now I don't know in which namespace I've to put this function. I considered the std namespace as a restricted area. Only total specialization and overloaded function templates are allowed to be added by the user. Putting it into the global namespace isn't "allowed" either in order to prevent pollution of the global namespace and clashes with other declarations. And finally putting this function into the namespace of the project doesn't work since the compiler won't find it there.

What had I best do? I don't want to write a new array class putted into the project namespace. Because in this case the compiler would find the right namespace via argument dependent name lookup. Or is this the only possible way because writing a new operator for existing classes means extending their interfaces and this isn't allowed either for standard classes?

+7  A: 

I fully support GMan and sbk who told you to use a named function instead of an operator. Contrary to popular believe, overloading operators is always almost wrong, because it almost never adds clarity to the code. There are surprisingly few exceptions. Among them are the stream input and output operators as well as the arithmetical operators should you implement a number-like type. (And just how likely is that outside of a book teaching you operator overloading?) Note that some people frown upon the std lib overloading + (and +=, of course) for std::string for the same reason (and others, like that a+b==b+a holds for numbers, but not for strings) - and IMO they do have a point.

Anyway if one wanted to do this despite all advice:
When you try to invoke the operator, the compiler tries to find it in the namespace it was invoked in, all enclosing namespaces, and the namespaces of all the arguments. (The latter is called argument-dependent lookup or Koenig lookup.) The namespace of the argument is std, which you must not add an overload to. So that leaves the namespace the operator is invoked in and its enclosing namespaces - including the global namespace, which encloses all others - to put the operator in.

So if you want to implement it despite all warnings, put it in the namespace where it is used in. If it is used in several namespaces, put it into the one that encloses all these. If that's the global namespace, so be it.

Oh, and did I mention you should not implement this as an overloaded operator?

sbi
phlipsy
And of course: You're right, the only way to stay with my overloaded operator seems to be to add it into the global namespace. What a pity!
phlipsy
"Better" strongly depends on your background, and the amount of operations performed. If bit arithmetics on arrays is a common issue, and you math background doesn't end at real numbers, overloading these operators makes sense. One could argue that it's a bad idea for STL containers, or that it's often applied incorrectly, but that's not an argument against operator overloading in general.
peterchen
Just as a side node, in math, commutativity and other properties also depend on the operands, not just the operators - though addition apparently always is commutative.
peterchen
@peterchen: It's more of a convention that the `+` sign represents a commutative operation. I think it stems from group theory. In rings the `+` usually represents the first operation and the `*` the second.
Matthieu M.
@peterchen: There is no argument against overloading operators _in general_, sometimes overloading is great. It's just that these occasions are rarer than most beginners think. A good rule of thumb might be to overload an operator if its use in the application domain is undisputed. (And it's not undisputed if people dispute despite the fact you think they shouldn't. If in doubt, use a function.)
sbi
+1  A: 

AFAIK, you can add the overload to any namespace (except std, because you are not allowed to add new functions to it), and you'd make it visible with a using declaration. This is how, for example, boost's assign library works (pay attention to the introductory snippets; for a precedent in the standard library, check out rel_ops). Also note that you can restrict the scope of using declarations, so as to avoid global pollution.

Example, using boost::array because of an old compiler:

#include <boost/array.hpp>
#include <functional>
#include <algorithm>
using boost::array;

namespace bitarray_operators
{
    template <size_t N>
    array<bool, N> operator& (const array<bool, N>& a, const array<bool, N>& b)
    {
        array<bool, N> result;
        std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::logical_and<bool>());
        return result;
    }
}

int main()
{
    using namespace bitarray_operators;      //<-- this makes it possible to find the & operator
    array<bool, 100> a, b, c;
    c = a & b;
}

I concur that overloading this operator might be somewhat questionable. It's also unclear to me why you don't use std::bitset which overloads this operator with potentially better performance possible because of a more compact internal representation (each bool taking one bit, instead of at least the size of a char).

visitor
phlipsy
+1 for the reference to the Boost.Assignment library and their way of handling these namespace problems.
phlipsy