Is it safe to assume that static_cast will never throw an exception?
For an int to Enum cast, an exception is not thrown even if it is invalid. Can I rely on this behavior? This following code works.
enum animal {
CAT = 1,
DOG = 2
};
int y = 10;
animal x = static_cast<animal>(y);
For this particular type of cast (integral to enumeration type), an exception might be thrown.
C++ standard 5.2.9 Static cast [expr.static.cast] paragraph 7
A value of integral or enumeration type can be explicitly converted to
an enumeration type. The value is unchanged if the original value is
within the range of the enumeration values (7.2). Otherwise, the
resulting enumeration value is unspecified / undefined (since C++17).
Note that since C++17 such conversion might in fact result in undefined behavior, which may include throwing an exception.
In other words, your particular usage of static_cast to get an enumerated value from an integer is fine until C++17 and always fine, if you make sure that the integer actually represents a valid enumerated value via some kind of input validation procedure.
Sometimes the input validation procedure completely eliminates the need for a static_cast, like so:
animal GetAnimal(int y)
{
switch(y)
{
case 1:
return CAT;
case 2:
return DOG;
default:
// Do something about the invalid parameter, like throw an exception,
// write to a log file, or assert() it.
}
}
Do consider using something like the above structure, for it requires no casts and gives you the opportunity to handle boundary cases correctly.
Is it safe to assume that static_cast will never throw an exception?
No. For user-defined types, the constructor and/or conversion operator might throw an exception, resulting in well-defined behavior.
Consider the output of this program:
#include <iostream>
struct A {
A(int) { throw 1; }
};
int main () {
int y = 7;
try {
static_cast<A>(y);
} catch(...) {
std::cout << "caught\n";
}
}
static_cast can't throw exception since static_cast is not runtime cast, if some cannot be casted, code will not compiles. But if it compiles and cast is bad - result is undefined.
(This answer focuses exclusively on the int to enum conversion in your question.)
For an int to Enum cast, an exception is not thrown even if it is invalid. Can I rely on this behavior? This following code works.
enum animal { CAT = 1, DOG = 2 };
int y = 10;
animal x = static_cast<animal>(y);
Actually, enums are not restricted to the list of enumerations in their definition, and that's not just some strange quirk, but a deliberately utilised feature of enums - consider how enumeration values are often ORed together to pack them into a single value, or a 0 is passed when none of the enumerations apply.
In C++03, it's not under explicit programmer control how big a backing integer will be used by the compiler, but the range is guaranteed to span 0 and the explicitly listed enumerations.
So, it's not necessarily true that 10 is not a valid, storable value for an animal. Even if the backing value were not big enough to store the integral value you're trying to convert to animal, a narrowing conversion may be applied - typically this will use however many of the least significant bits that the enum backing type can hold, discarding any additional high order bits, but for details check the Standard.
In practice, most modern C++03 compilers on PC and server hardware default to using a (32 bit) int to back the enumeration, as that facilitates calling into C library functions where 32 bits is the norm.
I would never expect a compiler to throw an exception when any value is shoehorned into an enum using static_cast<>.
Related
int main(){
int v = 1;
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#1
}
In this snippet, the expression ptr point to an object of type int, as per:
expr.static.cast#13
Otherwise, the pointer value is unchanged by the conversion.
Indirection ptr will result in a glvalue that denotes the object ptr point to, as per
expr.unary#op-1
the result is an lvalue referring to the object or function to which the expression points.
Access an object by using a glvalue of the permitted type does not result in UB, as per
basic.lval#11
If a program attempts to access ([defns.access]) the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:
a char, unsigned char, or std::byte type.
It seems it also does not violate the following rule:
expr#pre-4
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
Assume the width of char in the test circumstance is 8 bits, its range is [-128, 127]. The value of v is 1. So, Does it mean the snippet at #1 does not result in UB?
As a contrast, given the following example
int main(){
int v = 2147483647; // or any value greater than 127
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#2
}
#2 would be UB, Right?
It is the intention of the language that both snippets be implementation defined. I believe they were, until to C++17 which broke support for that language feature. See the defect report here. As far as I know, this has not been fixed in C++20.
Currently, the portable workaround for accessing memory representation is to use std::memcpy (example) :
#include <cstring>
char foo(int v){
return *reinterpret_cast<char*>(&v);
}
char bar(int v)
{
char buffer[sizeof(v)];
std::memcpy(buffer, &v, sizeof(v));
return *buffer;
}
foo is technically UB while bar is well defined. The reason is foo is UB is by omission. Anything the standard fails to define is by definition UB and the standard, in its current state, fails to define the behavior of this code.
bar produces the same assembly as foo with gcc 10. For simple cases, the actual copy is optimized out.
Regarding your rational, the reasoning seems sound except that, in my opinion, the rules defining unary operator* (expr.static.cast#13) doesn't have the effect you expect in this case. The pointer must point to the underlying representation, which is poorly defined as the linked defect describes. The fact that the pointer's value doesn't change does not mitigate the fact that it points to a different object. C++ allows objects to have the same address if their types are different, such as the first member in a standard layout class sharing the same address as the owning instance.
Note that the author is the defect report came to the same conclusion as you regarding snippet #1, but I disagree. But due to the fact that we are dealing with a language defect, and one that conflicts with state intentions, it is hard to definitively prove one behavior correct. The fundamental rules these arguments would be based on are known to be flawed in this particular case.
Does it mean the snippet at #1 does not result in UB?
Yes, the quoted rules mean that #1 is well defined.
#2 would be UB, Right?
No, as per the quoted rules, the behaviour of #2 is also well defined.
The type of ptr is char*, therefore the type of the expression *ptr is char whose value cannot exceed the value representable by char, thus expr#pre-4 does not apply.
Assume the width of char in the test circumstance is 8 bits, its range is [-128, 127].
This assumption is not necessary in order for #1 to be well defined.
The value of v is 1
This does not follow from the above assumption alone. It may be practically true in case of a little endian CPU (including the previous assumptions) although the standard doesn't specify the representation exactly.
#include <iostream>
typedef enum my_time {
day,
night
} my_time;
int main(){
// my_time t1 = 1; <-- will not compile
int t2 = night;
return 0;
}
How is it expected that I can assign an enum value to an int but not the other way in C++?
Of course this is all doable in C.
Implicit conversions, or conversions in general, are not mutual. Just because a type A can be converted to a type B does not imply that B can be converted to A.
Old enums (unscoped enums) can be converted to integer but the other way is not possible (implicitly). Thats just how it is defined. See here for details: https://en.cppreference.com/w/cpp/language/enum
Consider that roughly speaking enums are just named constants and for a function
void foo(my_time x);
It is most likely an error to pass an arbitrary int. However, a
void bar(int x);
can use an enum for special values of x while others are still allowed:
enum bar_parameter { NONE, ONE, MORE, EVEN_MORE, SOME_OTHER_NAME };
bar(NONE);
bar(SOME_OTHER_NAME);
bar(42);
This has been "fixed" in C++11 with scoped enums that don't implicitly convert either way.
You could cast to int. This expression makes an explicit conversion of the specified data type (int) and the given value (night).
int t2 = static_cast<int>(night)
Of course this is all doable in C
That doesn't mean that the minds behind C++ automatically consider it a desired behavior. Nor should they have such an attitude. C++ follows its own philosophy with regard to types. This is not the only aspect where a conscious decision was made to be more strongly typed than C. This valid C snippet is invalid in C++
void *vptr = NULL;
int *iptr = vptr; // No implicit conversion from void* to T* in C++
How is it expected that I can assign an enum value to an int but not the other way in C++?
It's the behavior because one side of the conversion is less error prone. Allowing an enumerator to become an integer isn't likely to break any assumptions the programmer has about an integer value.
An enumeration is a new type. Some of the enmueration's values are named. And for most cases of using an enumeration, we really do want to restrict ourselves to those named constants only.
Even if an enumeration can hold the integer value, it doesn't mean that value is one of the named constants. And that can easily violate the assumptions code has about its input.
// Precondition: e is one of the name values of Enum, under pain of UB
void frombulate_the_cpu(Enum e);
This function documents its precondition. A violation of the precondition can cause dire problems, that's what UB usually is. If an implicit conversion was possible everywhere in the program, it'd be that more likely that we violate the precondition unintentionally.
C++ is geared to catch problems at compile time whenever it can. And this is deemed problematic.
If a programmer needs to convert an integer an enumeration, they can still do it with a cast. Casts stand out in code-bases. They require a conscious decision to override a compiler's checks. And it's a good thing, because when something potentially unsafe is done, it should be with full awareness.
Cast the int when assigning . . .
my_time t1 = (my_time)1;
Is this legal C++ (>=14), resulting in a char being read and saved into aCode?
enum class ECode : char { Code1 = 'a' };
std::istream& operator>>(std::istream& aIn, ECode& aCode)
{
return aIn >> (std::underlying_type_t<ECode>&)aCode;
}
I would prefer return aIn >> static_cast<std::underlying_type_t<ECode>&>(aCode) which is not legal, it seems ("cannot cast to reference of unrelated type".)
However, this very similar line is legal, and is what my C-style cast should be identical to:
return aIn >> *static_cast<char*>(static_cast<void*>(&aCode))
As has been noted in comments, there is no strict aliasing violation because char can alias any type.
In practice I doubt any real compiler would do anything other than the "obvious" implementation, i.e. give the enum the same size and representation as the underlying type. In which case your reinterpret_cast would be well-defined and behave as expected.
However the Standard (as of C++17) does not appear to guarantee that.
As far as I can see, it only specifies that any value of the underlying type can be stored in the enum object, and that static_cast can be used losslessly for values in the range of the underlying type.
There is a language lawyer question here about whether sizeof(ECode) == sizeof(char) must hold, although the answers seem to say "the standard doesn't actually say so but they probably meant it".
But even if the size is the same there isn't a representation guarantee, e.g. the bits could be stored in some different order and the static_cast transforms the bits.
In [basic.fundamental] where it specifies the representation of integer types, it even has an explicit footnote saying that enumerations are not integer types.
The C++ standards mentions that reinterpret_cast is implementation defined, and doesn't give any guarantees except that casting back (using reinterpret_cast) to original type will result in original value passed to first.
C-style casting of at least some types behaves much the same way - casting back and forth results with the same value - Currently I am working with enumerations and ints, but there are some other examples as well.
While C++ standard gives those definitions for both cast-styles, does it also give the same guarantee for mixed casts? If library X returns from function int Y() some enum value, can use any of above casts, without worrying what cast was used to convert initial enum to int in Y's body? I don't have X's source code, so I cannot check (and it can change with next version anyway), and things like that are hardly mentioned in documentation.
I know that under most implementations in such cases both casts behave the same; my question is: what does C++ standard say about such cases - if anything at all.
C++ defines the semantic of the C cast syntax in terms of static_cast, const_cast and reinterpret_cast. So you get the same guaranteed for the same operation whatever syntax you use to achieve it.
reinterpret_cast can only be used for specific conversions:
Pointer to (sufficiently large) integer, and the reverse
Function pointer to function pointer
Object pointer to object pointer
Pointer-to-member to pointer-to-member
lvalue expression to reference
plus (conditionally) function pointer to object pointer and the reverse. In most cases, the converted value is unspecified, but there is a guarantee that a conversion followed by its reverse will yield the original value.
In particular, you can't use reinterpret_cast to convert between integer an enumeration types; the conversion must be done using static_cast (or implicitly, when converting an unscoped enumeration to an integer type), which is well defined for sufficiently large integer types. The only possible problem is if the library did something completely insane such as return reinterpret_cast<int&>(some_enum);
A C-style cast will perform either a static_cast or a reinterpret_cast, followed by a const_cast, as necessary; so any conversion that's well-defined by static_cast is also well-defined by a C-style cast.
No, reinterpret_cast is not equivalent to a C style cast. C style casts allow casting away const-volatile (so it includes the functionality of const_cast) not allowed in reinterpret_cast. If static_cast is allowed between the source and destination types, it will perform a static_cast which has different semantics than reinterpret_cast. It the conversion is not allowed, it will fallback to reinterpret_cast. Finally there is a corner case where the C cast cannot be represented in terms of any of the other casts: it ignores access specifiers.
Some examples that illustrate differences:
class b0 { int a; };
class b1 { int b; };
class b2 { int c; };
class d : public b0, public b1, b2 {};
int main() {
d x;
assert( static_cast<b1*>(&x) == (b1*)&x );
assert( reinterpret_cast<b1*>(&x) != (b1*)&x ); // Different value
assert( reinterpret_cast<b2*>(&x) != (b2*)&x ); // Different value,
// cannot be done with static_cast
const d *p = &x;
// reinterpret_cast<b0*>(p); // Error cannot cast const away
(b0*)p; // C style can
}
Can any one Give the example for
Boolean_conversions Using reinterpret_cast<>
Iam getting the execution failure..Any one explain ..
Ex:
void Boolean_Conversions()
{
bool b = 0;
int *i = reinterpret_cast<int*> (&b);
// iam expecting as *i is 0/false ,but it is showing runtime error
}
Edit:
In ISO Standard
Section $5.2.10, para 1:
"The result of the expression reinterpret_cast<T>(v) is the
result of converting the expression v to type T. If T is a
reference type, the result is an lvalue; otherwise, the re
sult is an rvalue and the l-value to r-value (4.1), array
to pointer (4.2), and function to pointer (4.3) standard c
onversions are performed on the the expression v. Types sh
all not be defined in a reinterpret_cast."
From this Iam trying ..I did many conversions ..like Array_to_Pointer,Function_To_Pointer,Integral/Pointer_Conversions ,etc...But i don't know why it is failing For Boolean Conversions.
Can any one tell why it is failing ..Otherwise Tell the Boolean COnversions using reinterpret_cast.
Mostly It is Giving Run time failure in G++.CC/EDG/COdepad..etc compilers
Please Explain
No, the output is not defined. Note that the conversion is undefined.
On my machine *i = 0xcccccc00
Note the lsb which is 00
which corresponds to the value of the bool variable initialized with 0(false). The byte pattern in the other three bytes (assuming the size of int as 32-bits) is completely unspecified and depends on what is there in that memory/endianness etc.
Technically, however that is an undefined behavior because we are deferencing a memory location (all of) which we did not reserve/allocate.
You've still not made it clear what you are trying to do! Are you aware for example that there are three casts:
TO_TYPE DEST_VAL = static_cast<TO_TYPE>(SOME_VAL);
TO_TYPE DEST_VAL = dynamic_cast<TO_TYPE>(SOME_VAL);
and the one you're hung up on:
TO_TYPE DEST_VAL = reinterpret_cast<TO_TYPE>(SOME_VAL);
Of these three, the last is the most dangerous, basically you are ignoring all the things the compiler is telling you about types and forcing your way on it! It implies that you know what you are doing, and in most cases, you don't.
Typically you should only use the last one as a LAST RESORT when the other two casts can't do what you need and you've carefully looked at the design, and consulted soothsayers etc.! It is NOT type safe, and IT MAKES NO GUARANTEES that the resulting object is what you expect it to be, as in what you are trying to do above.. bool and int may or may not have the same aligned storage and pointing to the address of a bool with a pointer that should point to an int and dereferencing could lead to potentially accessing all sorts of nether regions of memory...
bool b = 0;
int *i = reinterpret_cast<int*> (&b);
this will store the memory address of b to i after it is "converted" to a suitable style for an int pointer. if you want you cast a bool to int just do
int i = reinterpret_cast<int> (b);
if b is false, yes indeed it will be 0, otherwise it will be 1.
You dont need reinterpret_cast<int> though. Read the other comments made on the thread and it will become clear.