The fact that, in C++, T(x)
is exactly equivalent to (T)x
(and not to T temporary(x);
) is, in my experience, not widely known and even experts find it to be “surprising and unpleasant”. People instead think of T(x)
as “constructor syntax”. Which it effectively is for any number of arguments other than 1.
Note that this equivalence is true even when x
is a pack expansion with a single element. A variadic template that forwards to a constructor must use if constexpr(sizeof...(x) == 1)
, or a similar explicit handling mechanism, to substitute in a static_cast
for that case if it wants to avoid ever invoking a reinterpret_cast
or similar. Or it can constrain on std::is_constructible
which uses T x(args...);
.
Clang currently has no warnings relating to functional casts whatsoever. -Wold-style-cast
is purely syntax-based and never triggers on any functional cast, no matter how dubious. -Wcast-qual
also does not trigger on functional casts, which probably warrants a separate bug report. (GCC’s equivalent does warn.) [edit: I have made that bug report.]
I propose a warning -Wnon-static-functional-cast
which will trigger whenever T(x)
is not resolved to static_cast<T>(x)
under the explicit conversion rewrite rules in the standard. Any such casts that are desired should be explicitly spelled out with the relevant reinterpret_cast
or const_cast
s, and should not use a syntax that is widely understood to have a completely different meaning. static_cast
is semantically equivalent to the direct-initialization that occurs with T(x...)
for sizeof...(x) != 1
syntax, and for T y(x);
.
In case you were wondering, T{x}
is not a cast expression according to the language rules, so it does not suffer from any variant of this problem. Direct-list-initalization has no special case for one argument (except for deduction of auto
).
I know that -Weverything is not for production use. I use it in the above to demonstrate that Clang has literally no warnings that diagnose this at all.