As far as I read static_cast the following code should work:
#include <iostream>
#include <string>
class ConvSample
{
public:
template<typename T>
constexpr operator T(){
return {};
}
};
int main()
{
ConvSample aInst;
int i = aInst;
std::cout << i << "\n";
std::string str = static_cast<std::string>(aInst);
std::cout << str << "\n";
return 0;
}
And it works perfectly with some compilers like Clang. But e.g. with MSVC or ICC not.
See Compiler Explorer
In general they complain about some ambiguity caused by not really working constructors provided by std::string.
In addition if I turn on Wconversion on gcc I get a segmentation fault?!
Is there something wrong in the code? Are these errors just compiler bugs? If I change the code to not use templates it workes very well: Compiler Explorer
As we learnt in the generic types example, static_cast<> will fail if you try to cast an object to another unrelated class, while reinterpret_cast<> will always succeed by "cheating" the compiler to believe that the object is really that unrelated class.
Static Cast: This is the simplest type of cast which can be used. It is a compile time cast.It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones).
The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.
static_cast − This is used for the normal/ordinary type conversion. This is also the cast responsible for implicit type coersion and can also be called explicitly. You should use it in cases like converting float to int, char to int, etc. dynamic_cast −This cast is used for handling polymorphism.
The standard is unfortunately vague here ([expr.static.cast]/4, citations omitted):
An expression
e
can be explicitly converted to a typeT
if there is an implicit conversion sequence frome
toT
, or if overload resolution for a direct-initialization of an object or reference of typeT
frome
would find at least one viable function. […] [T]he result object is direct-initialized frome
. […]
Both of the enabling conditions hold here: there is an implicit conversion sequence (consisting of the desired conversion function call), and there are several viable functions for direct-initialization (because there are also implicit conversion sequences for the various single parameters for std::string
constructors).
However, it is only the copy-initialization of the implicit conversion sequence, which refuses to convert ConvSample
to (say) const char*
and then to std::string
, that provides an unambiguous means of producing a std::string
: it specifically looks for conversion functions to the target type, and while it allows conversions to (say) const std::string&
, common implementations do not interpret that to mean that the conversion function template should be instantiated for that type as well and become ambiguous.
The direct-initialization that is ultimately called for is ambiguous among std::string
’s 5 single-argument constructors (6 for std::string_view
-like types): ConvSample
can of course be converted to the parameter type for any of them at the same “cost”.
The compilers that accept this are applying copy-initialization rules (but still allowing explicit
conversions). Those that reject it are applying direct-initialization, which I believe is what the wording currently requires. The reference to implicit conversion sequences was introduced only in C++17 for CWG242, and apparently implementation divergence persists in this area.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With