Certain language constructs require that an expression be converted to a Boolean value. An expression
e
appearing in such a context is said to be contextually converted tobool
and is well-formed if and only if the declarationbool t(e);
is well-formed, for some invented temporary variablet
(11.6).
Consider now the snippet below. It doesn't compile, neither in clang, GCC or VS.
struct A{ bool operator!() { return true; } };
int main(){
A a;
bool t(a);
}
Thus, from [conv]/4 we conclude that the type A
is not contextually converted to bool
.
The operand of the logical negation operator
!
is contextually converted tobool
(Clause 7); its value istrue
if the converted operand isfalse
andfalse
otherwise. The type of the result isbool
.
My understanding of the paragraph above is that the operand of the logical negation operator !
must be contextually converted to bool
. We have just concluded that the type A
is not contextually converted to bool
. Therefore, from [expr.unary.op]/9, we can say that the following code should not compile. But it does, in clang, GCC and VS.
struct A{ bool operator!() { return true; } };
int main(){
A a;
bool t = !a;
}
What am I missing?
[expr] as a whole applies to built-in operators:
Clause [expr] defines the effects of operators when applied to types for which they have not been overloaded.
The definition in [expr.unary.op] is simply the definition of the built-in operator!
. Additionally, [over.match.oper] describes how to look up overloaded operators:
For a unary operator @ with an operand of a type whose cv-unqualified version is T1, [...], three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows: [...]
For !a
, you'd have two candidates: your overload A::operator!()
and the built-in defined in [over.built]:
There also exist candidate operator functions of the form
bool operator!(bool);
For the built-in to be selected by overload resolution, the type would have to be contextually converted to bool
† as your argument suggests. However, this candidate is not viable - but the overloaded member operator is.
†T.C. is on top of it as usual, pointing out cwg issue 1919 which indicates that a type that is contextually convertible to bool
still shouldn't use the builtin operator!
due to a wording issue. Although, both gcc and clang permit it (which is what we all want to happen, probably).