Namespaces and Operator Overloading in C++

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.

C++ operator overloading and associated namespace

This is caused by two different CWG issues: CWG issue 2052 and CWG issue 1391.

First, CWG 1391. On encountering x + a, the usual name lookup finds, among other overloads,

template <typename T> double operator+ (T, double);

Template argument deduction is performed by match T to the type of the lhs of +, which is double, so this deduces T to be double. The second parameter's type contains no template parameter, so is not considered under current rules. To be sure, N::A can't be converted to a double, so the resulting specialization is not viable, but the current rules say that template argument deduction doesn't care about this; that will be handled in overload resolution.

The proposed resolution to CWG 1391, among other things, adds a new paragraph to the standard:

If deduction succeeds for all parameters that contain
template-parameters that participate in template argument deduction,
and all template arguments are explicitly specified, deduced, or
obtained from default template arguments, remaining parameters are
then compared with the corresponding arguments. For each remaining
parameter P with a type that was non-dependent before substitution of
any explicitly-specified template arguments, if the corresponding
argument A cannot be implicitly converted to P, deduction fails.
[Note: Parameters with dependent types in which no template-parameters
participate in template argument deduction, and parameters that became
non-dependent due to substitution of explicitly-specified template
arguments, will be checked during overload resolution. —end note]

In other words, if an argument (a in our case) corresponding to a non-dependent parameter (double) cannot be converted to the parameter's type, deduction would simply fail. So in our case, post-CWG1391 template argument deduction will fail for this overload, and everything would be well.

Clang implements the current rules, however, so deduction succeeds with T = double, substitution occurs, and we encounter CWG 2052. Quoting the writeup from Richard Smith (a Clang dev):

In an example like

  struct A { operator int(); };
template<typename T> T operator<<(T, int);
void f(A a) { 1 << a; }

Template argument deduction succeeds for the operator template,
producing the signature operator<<(int,int). The resulting
declaration is synthesized and added to the overload set, per 14.8.3
[temp.over] paragraph 1. However, this violates the requirement of
13.5 [over.oper] paragraph 6,

An operator function shall either be a non-static member function or
be a non-member function that has at least one parameter whose type is
a class, a reference to a class, an enumeration, or a reference to an
enumeration.


This is not a SFINAE context, so the program is ill-formed, rather
than selecting the built-in operator.

In this case, there's no conversion, so the deduced operator+(double, double) is actually not viable, but non-viable candidates are not eliminated until you have built the candidate set, and here building the candidate set is causing a hard error.

The proposed resolution to CWG 2052 will make this case SFINAE instead - also making the original code work. The problem is - Clang is implementing the current version of the standard here, too.

Overloading an operator in a namespace and a sub-namespace in C++17 is ambiguous

I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.

This is because of Argument Dependent Lookup (ADL).

If you pull the operator from the debug namespace into the current scope via the using, this does not "overwrite" the existing operator, they are both avaliable, hence the ambiguity.

There are many ways to deal with it, one possibility is to use a different type for the debug output:

namespace test {
namespace debug {
struct debug_A {
const A& data;
debug_out(const A& a) : a(a) {}
};

std::ostream& operator<< (std::ostream& os, const debug_A& d) {
auto& a = d.data;
std::string info = "\n\tDebug\n"
"\t\tLine: " + std::to_string(__LINE__) + "\n"
"\t\tFile: " __FILE__ "\n"
"\t\tDate: " __DATE__ "\n"
"\t\tTime: " __TIME__ "\n"
"\t\tVersion: " + std::to_string(__cplusplus) + "\n";
return a.toStream(os) << info;
}
}
}

Now you can call it via

std::cout << test::debug::debug_A{ a } << '\n';

Operator overloading and namespaces

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.

How to overload the operator from within a namespace

You're expecting using namespace EU; to put all the subsequent code inside namespace EU, but it won't (otherwise your int main would be in the namespace too!). This just brings things already in that namespace into scope.

It means that you're declaring the friend function inside the namespace, but defining a new function in the global scope. Calls to the former will fail because there's no definition for it.

Remove the using namespace, and wrap namespace EU { } around everything in euro.cpp.

How can I specify an overloaded operator in a different namespace?

The rules are quite complicated, and I myself don't fully grasp them, but let's see if we can make head or tails of them (I think we can):

namespace nx {
struct X {};
}

namespace ns {
auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X')
}

auto operator==(nx::X, nx::X) { return true; }

auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}

This isn't found for a simple reason: operator== is not declared before its use. Nothing to to with ADL yet. So far so good. We understand that. Let's fix it:

namespace nx {
struct X {};
}

auto operator==(nx::X, nx::X) { return true; }

namespace ns {
auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
}

auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}

Does this work? Yes, it does, it compiles and calls our operator==. Is this the correct solution? No!. Because if we add this:

namespace nx {
struct X {};
}

auto operator==(nx::X, nx::X) { return true; } // (1)

namespace ns {

template <class T> auto operator==(T, int) { return false; } // (2)

auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X')
}

auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}

Then (2) in ns hides (1) in global namespace, even if (1) would be a better fit. This is called name hiding and - again - doesn't involve ADL in any way.

Even worse:

namespace nx {
struct X {};
}

auto operator==(nx::X, nx::X) { return true; } // (1)

namespace ns {

template <class T> auto operator==(T, T) { return false; } // (2)

auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (2)
}

auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}

Would compile and silently calls (2) instead of our operator (1).

For a real world context think of namespace ns as namespace std and
any operator declared inside std. And you've got the situation in your post.

The correct solution is:

namespace nx {
struct X {};
auto operator==(nx::X, nx::X) { return true; } // (1)
}

namespace ns {

template <class T> auto operator==(T, T) { return false; } // (2)

auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (1)
}

auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}

What happens here is that ADL kicks in and brings (1) from nx and now (1) is considered alongside (2). But (1) is more specialized than (2) and so (1) is correctly selected.

If you don't have control of namespace nx and can't add the operator there, then what I can advise is to use callables instead of relying on operators. E.g instead of std::find use std::find_if with your own predicate (lambda) where you control exactly which method/operator to call. And when I say "exactly" I mean exactly: i.e. ::operator==(x1, x2) (or whatever namespace you declared them) instead of x1 == x2.


You can read more on this great article by Herb Sutter Namespaces & Interface Principle

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.

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(); }
}

SFINAE-based Operator Overloading across Namespaces

Your function cannot be found by ADL, you might add some using to allow to use it:

using ONE::operator|;
TWO::A a1 = TWO::A::x | TWO::A::y;

Demo

using namespace ONE; might be an alternative too.



Related Topics



Leave a reply



Submit