Consider the following:
struct C {
explicit operator bool() const {
return true;
}
};
int main() {
C c;
auto b = static_cast<const bool &>(c);
return 0;
}
Clang++ 18 compiles fine, G++ 14 says:
test.cpp: In function ‘int main()’:
test.cpp:12:14: error: invalid ‘static_cast’ from type ‘C’ to type ‘const bool&’
12 | auto b = static_cast<const bool &>(c);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
See on Compiler Explorer.
This incompatibility arose in my code because Catch2 test macros use that kind of static_cast
under the hood and it fails with a custom type with an explicit operator bool
, but only with G++.
It seems to me that since a bool
temporary can bind to a const bool &
, this should be allowed.
Who's right here, following the standard?
Clang is right.
When you type operator bool()explicit
the complete restriction that explicit
causes is described here:
A conversion function may be explicit (
[dcl.fct.spec]
), in which case it is only considered as a user-defined conversion for direct-initialization ([dcl.init]
). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.
static_cast
does not involve assignments, just initializations. Are the initializations that occur in a static_cast
direct or not?
The initialization that occurs
- (15.1) for an initializer that is a parenthesized expression-list or a braced-init-list,
- (15.2) for a
new
-initializer ([expr.new]
),- (15.3) in a
static_cast
expression ([expr.static.cast]
),- (15.4) in a functional notation type conversion (
[expr.type.conv]
), and- (15.5) in the braced-init-list form of a condition
is called direct-initialization.
the standard explicitly states that initialization that occurs in a static_cast expression is a direct-initialization.
So explicit
cannot restrict how operator bool()
works in a static_cast
. (ok, it could restrict it if we ended up having a concept restriction that queried assignment properties of the right hand side type with being assigned to a bool, but I'm not even sure HOW I'd write that!)
What is going on when you static cast to a reference?
If
T
is a reference type, the effect is the same as performing the declaration and initializationT t(E);
for some invented temporary variablet
([dcl.init]
) and then using the temporary variable as the result of the conversion.
This initialization, by [dcl.init.general]/15
, is a direct-initialization, so operator bool()const explicit
is permitted to be used.
I don't think it is in doubt that
struct foo {
operator bool()const{return true;}
};
foo f;
bool const& b = f;
is perfectly legal; so, clang is right, the other compilers are in error.
...
Apparently there is doubt that bool const& b = f;
is legal when f
has a non-explicit
operator bool()const
. So here is my evidence it is legal:
bool const& b = f;
is legal due to [dcl.init]/5.1.2 which permits binding to a const-lvalue-ref-to-T from an object that can be converted to an rvalue-to-T.
bool const& b = f;
is safe to due "temporary materialization" and reference lifetime extension causing the materialized temporary to last as long as the const-lvalue-ref-to-T does.
Note that the use of =
here would not be legal if the operator bool
was explicit, it would have to be bool const& b(f);
if f
has an explicit operator bool
; however, this section merely exists to point out that without explicit
there is no problem. Prior logic shows that explicit
does not cause a problem, as the initialization in a static_cast
is, well, an explicit one.