I came across a strange situation today, where declaring a deleted operator with certain arguments changed the behaviour of seemingly unrelated code.
I reduced it to the following. Start with this:
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
void Waldo()
{
C(A | B);
}
}
Notice that C has two constructors, a public one and a private one. This code compiles, indicating that the public overload is being chosen, so the expression A | B
A | B
has type E
. In turn this means that the operator|(N::E, N::E)
has been matched (otherwise A
and B
would undergo implicit conversion to integers, the type of A | B
would be int
, and the private constructor would be matched.
So far so good. Now I define a new enumeration type F
, and a deleted operator|
that involves F:
namespace N
{
enum E { A, B };
struct C
{
C(E);
private:
C(int);
};
}
N::E operator|(N::E, N::E);
namespace N
{
enum F {};
int operator|(F, int) = delete;
void Waldo()
{
C(A | B);
}
}
Now the code doesn't compile, saying that C(int)
is private. This indicates that now A | B
A | B
has type int
, which means operator|(N::E, N::E)
is no longer being matched.
Why did the addition of the deleted operator|(F, int)
stop operator|(N::E, N::E)
from being matched?
First off, note that being declared as delete
d is irrelevant, since deleted functions still take part in overload resolution.
Now, on to overload resolution. Cf. 13.3.1.2/3:
three sets of candidate functions, designated member candidates , nonmember candidates and built-in candidates , are constructed
(There are no member candidates, since E
is not a class type.) We know from that the operator overload is found by unqualified lookup . So when we consult 3.4.1 ("Unqualified lookup"), we find that
name lookup ends as soon as a declaration is found for the name.
Since you introduce the second operator overload within the namespace N
, it is found first, and name lookup stops. At this point, the overload set consists of your int N::operator|(N::F, int)
and the built-in operators. Continuing in 13.3.1.2/6:
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates.
Only the builtin is viable (since you cannot convert E
to F
implicitly), and thus it is chosen.
The solution to your problem is simple.
Put your operator|
in the same namespace as the type. Now, ADL (argument dependent lookup) kicks in, and it is found even if there is the unrelated operator|
also visible.
Live example . Note that N::operator|
is found despite the |
being used in namespace Z
.
The proper place to overload free operators for a type is the namespace
that the type lives in, not the global namespace.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.