Namespaces and Operator Resolution

Namespace resolution with operator== in the STL

!(*__first1 == *__first2) takes place in std::operator==, a function template, so it is considered a dependent unqualified function call expression, so during overload resolution only functions found within the definition context of std::operator== and those found via ADL are candidates.

Clearly there are no operator==(const Foo&, const Foo&) declared within the context of the definition of the standard comparison operator. In an argument dependent lookup (ADL) the namespaces of each of the arguments are checked to search for a viable function for the call, so this is why defining operator== inside of ANamespace works.

Overload resolution for template operators in namespaces

The operator+ of Complex can be declared as friend function in Complex, which does not pollute the global namespace. Your example should compile after the following change.

struct Complex {
Complex(){};
explicit Complex(const DoubleArray<2>& components);

DoubleArray<2> _components;

friend Complex operator+(const Complex& rhs, const Complex& lhs) { return Complex(); }
};

According to C++ standard working draft N4140,

When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded.

In your case, the two operator+ functions are declared in different namespace and thus are not qualified for overload resolution.

When compiler finds the first match Complex operator+(const Complex& rhs, const Complex& lhs), DoubleArray cannot be implicitly converted to Complex. Therefore, you got the no match for ‘operator+’ error.

How does the operator overload resolution work within namespaces?

This is a name hiding issue. The standard says (c++03, 3.3.7/1)

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived
class (10.2).

The "name" on your case would be operator>> and namespaces constitute nested declarative regions.

The easiest way to fix that would be to use a using declaration where you declare the namespace-local operator<<:

namespace your_namespece {
std::istream& operator>>(std::istream& is, SomeClass& obj) { return is; };
using ::operator>>;
}

Note that this feature doesn't interfere with Koenig lookup (at least in your case, in principle, it can), so IO operators from std:: will still be found.

PS: Another possibility for working aroud this issue would be defining the operator for SomeClass as an inline friend. Such functions are declared at the namespace level (outside of "their" class), but are not visible from there. They can only be found by Koenig lookup.

Operator overloading, name resolution and namespaces

In the templated case, when compiling f2, the compiler considers using Foo::operator* but not Bar::operator*, while in the non-templated case, it considers using both (and refuses to go further because of the ambiguity). What makes the compiler behave differently ?

In both cases the compiler considers using both, but in the case of a templated operator*, the call is not ambiguous since there is a non-templated function which parameter types perfectly matches the arguments (try replace 3.f with 3. and you will see that the templated version is found). Typically:

template <typename T>
void g (T) { }

void g (float) { }

g(0.f); // Ok, the overload for float is preferred over the templated version

A user of my library Bar will be outside the Bar:: namespace, yet I want Bar::operator* to be used, and not Foo::operator*. I considered explicitely calling Bar::operator*(3.f,a), which is ugly, or inserting my own operator in the global namespace, which I reckon is a Bad Thing. Is there an option I am missing, or am I doing something wrong ?

Unfortunately, ADL will not find your overload since the only parameters of operator* are float and MyDeriv which are defined inside the namespace Foo. One possible way would be to inherit from Foo::Deriv:

namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}

Another one is to declare your overload for operator* inside the Foo namespace:

namespace Bar {
typedef Foo::Deriv MyDeriv;
}

namespace Foo {
Bar::MyDeriv operator* (float x, const Bar::MyDeriv& d) { return Bar::MyDeriv(); }
}

Namespaces and Operator Overloading in C++

You should define them in the library namespace.
The compiler will find them anyway through argument dependant lookup.

No need to pollute the global namespace.

operator overload resolution (C++)

As namespace b is nested inside namespace a, names declared in b will hide names declared in a. When name lookup occurs in a::b::print, it searches b, and if it doesn't find what it's looking for continues searching in a. So it does find operator<< in b and stops looking, and does not even consider the proper one in a. When you comment it out, it doesn't find it in b, and continues searching into a and finds it. You could fix this by adding this in namespace b:

using a::operator<<; 

However, the real problem with your code is that you're not utilizing ADL (argument dependent lookup) properly. Operators that work on user defined types should be in the same namespace as the types they operate on. What ADL does is add extra namespaces to search, so that any arguments (or template parameters) involved with the call will have their namespace considered--automatically. (This is why you can use the operators provided in the std namespace for builtin types when your code is outside std.)

So move operator<< out of namespace a and into the same (global) namespace where Foo is. Or, probably better, move Foo into namespace a.

Template overload resolution for operators inside an anonymous namespace

This is definitely a bug in gcc. The code below rightly prints right with clang but wrong with GCC.

#include <iostream>

template<typename T> struct A{ T t; };
struct B{};
struct C : public B{};

std::ostream& operator<< (std::ostream& out, const B&)
{ return out << "right"; }

namespace {
template<typename T>
std::ostream& operator<< (std::ostream& out, const A<T>&v)
{ return out << v.t; }

std::ostream& operator<< (std::ostream& out, const C&)
{ return out << "wrong"; }
}

int main(){
A<C> a;
std::cout << a;
}

Reported here.



Related Topics



Leave a reply



Submit