[expr.unary.op]/9 似乎暗示 `operator !()` 不能应用于下面的类型 A。但编译器不同意这一点

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

现在考虑下面的片段。它不编译,也不在 clang, GCC or VS.

struct A{ bool operator!() { return true; } };
int main(){
    A a;
    bool t(a);
}

因此,从 [conv]/4 我们得出结论,类型 A 而不是 上下文转换为 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.

我对上面这段的理解是,逻辑非运算符!的操作数必须根据上下文转换为bool。我们刚刚得出结论,类型 A 而不是 上下文转换为 bool。因此,从[expr.unary.op]/9,我们可以说下面的代码应该编译。但确实如此,在 clang, GCC and VS.

struct A{ bool operator!() { return true; } };
int main(){
    A a;
    bool t = !a;
}

我错过了什么?

[expr]整体适用于内置运算符:

Clause [expr] defines the effects of operators when applied to types for which they have not been overloaded.

[expr.unary.op]中的定义只是内置operator!的定义。此外,[over.match.oper] 描述了如何查找重载运算符:

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: [...]

对于 !a,您有两个候选对象:您的重载 A::operator!()[over.built] 中定义的内置:

There also exist candidate operator functions of the form

bool operator!(bool);

对于通过重载决议选择的内置函数,类型必须根据上下文转换为 bool 正如您的论点所建议的那样。然而,这个候选者是不可行的——但是重载的成员运算符是可行的。


is on top of it as usual, pointing out cwg issue 1919 这表明 上下文可转换为 bool 的类型仍然不应该由于措辞问题,请使用内置 operator!。尽管 gcc 和 clang 都允许它(这可能是我们都希望发生的事情)。