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_castexpression ([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
Tis 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.