Reputation: 668
For reasons I do not understand, the following C++ code fails to compile on VS 2022 (dialect set to C++20):
#include <compare>
namespace N1
{}
namespace N1::N2
{
class A {};
A operator-(A&);
}
std::strong_ordering operator-(std::strong_ordering o);
namespace N1
{
using namespace N2; // (1) !!!
std::strong_ordering foo();
inline std::strong_ordering bar()
{
return -foo(); // (2) !!!
}
}
At (2), the compiler files a complaint: error C2678: binary '-': no operator found which takes a left-hand operand of type 'std::strong_ordering' (or there is no acceptable conversion)
.
When the using namespace
directive at (1) is removed, the compiler happily finds the operator-
defined at global scope for the std::strong_ordering
type.
This gives rise to a set of questions:
operator-
at global scope?using namespace
directive is there to stay?Upvotes: 0
Views: 152
Reputation: 119069
Your compiler is correct, and I would expect other compilers to agree.
For the behaviour of a using-directive, see C++20 [namespace.udir]/2:
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (6.5.2), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
In other words, if N1
contains using namespace N2;
, then only for the purposes of unqualified name lookup, names in N2
will appear as if they are in the lowest common ancestor namespace of N1
and N2
. Since N2
is inside N1
, the lowest common ancestor namespace is just N1
, and that means the operator-
in N2
appears inside N1
when unqualified name lookup is performed.
This means that unqualified lookup for operator-
will find N2::operator-
, and it won't proceed to the global namespace to continue searching for additional declarations. See [basic.lookup.unqual]/1:
In all the cases listed in 6.5.2, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
To work around the issue, there are two strategies. One is to place the operator-
for your type in the namespace where that type is declared, so it can be found via argument-dependent lookup. However, you are not allowed to add operator overloads to the std
namespace. The other strategy is to redeclare the operator-
you want using a using-declaration:
using namespace N2;
using ::operator-;
This effectively brings the operator-
that you want "one level deeper", putting it at the same level as the other operator-
that appears thanks to the using-directive, so unqualified name lookup will find both, and the compiler will perform overload resolution.
Upvotes: 8
Reputation: 668
I'm now working around the issue by having the operator-
live in std::
, i.e.:
...
namespace std
{
strong_ordering operator-(strong_ordering o);
}
...
However, I'm not really sure whether it is good practice to mess with the std::
namespace; comments welcome.
Upvotes: 0