Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

[expr.unary.op]/9 seems to imply that the `operator !()` could not be applied to the type A below. But compilers disagree with that

[conv]/4:

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 to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (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.

[expr.unary.op]/9:

The operand of the logical negation operator ! is contextually converted to bool (Clause 7); its value is true if the converted operand is false and false otherwise. The type of the result is bool.

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?

like image 231
Belloc Avatar asked Jun 21 '17 14:06

Belloc


1 Answers

[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).

like image 137
Barry Avatar answered Oct 13 '22 00:10

Barry