Why would `(int)` precede an assignment in C++? - c++

Here is a MWE of something I came across in some C++ code.
int a = (int)(b/c);
Why is (int) after the assignment operator?
Is it not recommended to use it like this?

This is simply a C-style typecast. It is used to make the author's intentions explicit, especially when the result of b/c is of another type (such as unsigned or float).
Without the cast, you will often get a compiler warning about an implicit conversion which can sometimes have consequences. By using the explicit cast, you are stating that you accept this conversion is fine within whatever other limits your program enforces, and the compiler will perform the conversion without emitting a warning.
In C++, we use static_cast<int>(b/c) to make the cast even more explicit and intentional.

This is a cast used to convert a variable or expression to a given type. In this case if b and c were floating point numbers, adding the cast (int) forces the result to an integer.
Specifically this is a "C style cast", modern C++ has some additional casts to given even more control (static_cast, dynamic_cast, const_cast etc)

It is not "(int) after the assignment operator".
It is "(int) before a float - the result of b/c".
It casts the float to an int.

This is a mistake. In the code:
int a = b/c;
then it may cause undefined behaviour if the result of the division is a floating point value that is out of range of int (e.g. it exceeds INT_MAX after truncation). Compilers may warn about this if you use warning flags.
Changing the code to int a = (int)(b/c); has no effect on the behaviour of the code, but it may cause the compiler to suppress the warning (compilers sometimes treat a cast as the programmer expressing the intent that they do not want to see the warning).
So now you just have silent undefined behaviour, unless the previous code is designed in such a way that the division result can never be out of range.
A better solution to the problem would be:
long a = std::lrint(b/c);
If the quotient is out of range then this will store an unspecified value in a and you can detect the error using floating point error handling. Reference for std::lrint

Related

Don't use static cast for arithmetic conversions (cpp-core-guidelines)

msvc's code analyzer for the cpp core guidelines tells me
Warning C26472 Don't use a static_cast for arithmetic conversions. Use
brace initialization, gsl::narrow_cast or gsl::narrow
(type.1).
for this snippet
static_cast<IntType>(static_cast<unsigned long long>(hexValue(digit)) << (digitIdx * 4));
Why shouldn't I use static_cast here?
Also, with brace init this would look like this
IntType{unsigned long long{hexValue(digit)} << (digitIdx * 4)};
which doesn't look any better imo. This looks more like a function style cast than anything else.
I cannot use gsl and I think gsl::narrow is a wrapper around static_cast itself, so is this purely a readability issue here?
so is this purely a readability issue here?
Nope. Brace initialization prohibits narrowing conversions, and will cause a diagnostic. The guideline's purpose is to help protect code from unintended narrowing. A static_cast will allow a narrowing conversion through silently.
You seem to be dealing in integers, so short of using an extended integer type that is larger than unsigned long long, you'll probably not hit any narrowing.
But the guideline is there for the general case, and it's better to write code consistently, even when there is no actual risk.
Here is a link to Microsoft documentation:
https://learn.microsoft.com/en-us/cpp/code-quality/c26472?view=vs-2019
gsl::narrow ensures lossless conversion and causes run-time crash if it is not possible.
gsl::narrow_cast clearly states that conversion can lose data and it is acceptable.
static_cast doesn't perform these checks, so it is safer to explicitly state your intentions.

Can wrapping a type conversion in a static_cast ever hurt?

I'm updating some old code and I get hundreds of warnings along the lines of warning C4244: '+=': conversion from 'std::streamsize' to 'unsigned long', possible loss of data in Visual Studio.
The project compiles and runs fine when just ignoring the warnings, but I want to remove them and put a static_cast<unsigned long>() function around each. Considering the code runs fine now, could this potentially be harmful?
Yes, static_cast can hurt, as it will tell the compiler to shut up, as you know what you are doing. The question is whether you actually know what you are doing?
Obviously, casting to a smaller type can result in an unexpected result if the stored data exceeds the smaller types size. Use a static_cast if you know for sure, that this case won't ever happen or that you expect a truncated value. If not, keep the warning until you have properly designed your code.
The project compiles and runs fine when just ignoring the warnings
To start with, never ignore warnings. Think over what your code actually does instead.
Considering the code runs fine now, could this potentially be harmful?
Regarding your particular case, a static_cast<unsigned long> from std::streamsize will be harmful.
As the reference documentation of std::streamsize says, it's intentionally a signed type:
The type std::streamsize is a signed integral type used to represent the number of characters transferred in an I/O operation or the size of an I/O buffer. It is used as a signed counterpart of std::size_t, similar to the POSIX type ssize_t.
Static casting in that case actually means loss of semantics.
The idea is that conversions allowed by static_cast are somewhat less
likely to lead to errors than those that require reinterpret_cast. In
principle, it is possible to use the result of a static_cast without
casting it back to its original type, whereas you should always cast
the result of a reinterpret_cast back to its original type before
using it to ensure portability.
from Bjarne Stroustrup's C++ Style and Technique FAQ
Be careful with consts. static_cast doesn't cast away const.
In general, I think it would be better to rewrite your code instead of "casting every problem away" in hope that everything goes well.

Is there a gcc flag to allow C-style implicit pointer conversions in C++?

I'm drowning in the sea of casts between char* and unsigned char*, it's just ridiculous. There has to be some way around this.
Yes -fpermissive will allow this
No. C++ pointers are more strongly typed than C's. You are required to cast between pointer types outside of upcasting in C++ which can happen implicitly. What you want is intentionally difficult in C++ to discourage people from doing so.
C allows these potentially unsafe conversions to occur silently, but if you enable warnings in gcc, the issues will be brought to your attention.
On another note: having a char * point to an unsigned char is well defined behavior, though could be error prone in the long run depending on how the memory is used.

Why does C++ allow implicit conversion from int to unsigned int?

Consider following code:
void foo(unsigned int x)
{
}
int main()
{
foo(-5);
return 0;
}
This code compiles with no problems. Errors like this can cause lots of problems and are hard to find. Why does C++ allow such conversion?
The short answer is because C supported such conversions originally and they didn't want to break existing software in C++.
Note that some compilers will warn on this. For example g++ -Wconversion will warn on that construct.
In many cases the implicit conversion is useful, for example when int was used in calculations, but the end result will never be negative (known from the algorithm and optionally asserted upon).
EDIT: Additional probable explanation: Remember that originally C was a much looser-typed language than C++ is now. With K&R style function declarations there would have been no way for the compiler to detect such implicit conversions, so why bother restricting it in the language. For example your code would look roughly like this:
int foo(x)
unsigned int x
{
}
int main()
{
foo(-5);
return 0;
}
while the declaration alone would have been int foo(x);
The compiler actually relied on the programmer to pass the right types into each function call and did no conversions at the call site. Then when the function actually got called the data on the stack (etc) was interpreted in the way the function declaration indicated.
Once code was written that relied on that sort of implicit conversion it would have become much harder to remove it from ANSI C even when function prototypes were added with actual type information. This is likely why it remains in C even now. Then C++ came along and again decided to not break backwards compatibility with C, continuing to allow such implicit conversions.
Just another quirk of a language that has lots of silly quirks.
The conversion is well-defined to wrap around, which may be useful in some cases.
It's backward-compatible with C, which does it for the above reasons.
Take your pick.
#user168715 is right. C++ was initially designed to be a superset of C, pretending to be as backward-compatible as possible.
The "C" philosophy is to deliver most of the responsibility to the programmer, instead of disallowing dangerous things. For C programmers it is heaven, for Java programmers, it is hell... a matter of taste.
I will dig the standards to see where exactly it is written, but I have no time for this right now. I'll edit my answer as soon as I can.
I also agree that some of the inherited freedom can lead to errors that are really hard to debug, so I am adding to what was said that in g++ you can turn on a warning to prevent you from doing this kind of mistake: -Wconversion flag.
-Wconversion
Warn for implicit conversions that may alter a value. This includes
conversions between real and integer,
like abs (x) when x is double;
conversions between signed and
unsigned, like unsigned ui = -1; and
conversions to smaller types, like
sqrtf (M_PI). Do not warn for explicit
casts like abs ((int) x) and ui =
(unsigned) -1, or if the value is not
changed by the conversion like in abs
(2.0). Warnings about conversions
between signed and unsigned integers
can be disabled by using
-Wno-sign-conversion.
For C++, also warn for confusing overload resolution for user-defined
conversions; and conversions that will
never use a type conversion operator:
conversions to void, the same type, a
base class or a reference to them.
Warnings about conversions between
signed and unsigned integers are
disabled by default in C++ unless
-Wsign-conversion is explicitly enabled.
Other compilers may have similar flags.
By the time of the original C standard, the conversion was already allowed by many (all?) compilers. Based on the C rationale, there appears to have been little (if any) discussion of whether such implicit conversions should be allowed. By the time C++ came along, such implicit conversions were sufficiently common that eliminating them would have rendered the language incompatible with a great deal of C code. It would probably have made C++ cleaner; it would certainly have made it much less used -- to the point that it would probably never have gotten beyond the "C with Classes" stage, and even that would just be a mostly-ignored footnote in the history of Bell labs.
The only real question along this line was between "value preserving" and "unsigned preserving" rules when promoting unsigned values "smaller" than int. The difference between the two arises when you have (for example) an unsigned short being added to an unsigned char.
Unsigned preserving rules say that you promote both to unsigned int. Value preserving rules say that you promote both values to int, if it can represent all values of the original type (e.g., the common case of 8-bit char, 16-bit short, and 32-bit int). On the other hand, if int and short are both 16 bits, so int cannot represent all values of unsigned short, then you promote the unsigned short to unsigned int (note that it's still considered a promotion, even though it only happens when it's really not a promotion -- i.e., the two types are the same size).
For better or worse, (and it's been argued both directions many times) the committee chose value preserving rather than unsigned preserving promotions. Note, however, that this deals with a conversion in the opposite direction: rather than from signed to unsigned, it's about whether you convert unsigned to signed.
Because the standard allows implicit conversion from signed to unsigned types.
Also (int)a + (unsigned)b results to unsigned - this is a c++ standard.

static_cast wchar_t* to int* or short* - why is it illegal?

In both Microsoft VC2005 and g++ compilers, the following results in an error:
On win32 VC2005: sizeof(wchar_t) is 2
wchar_t *foo = 0;
static_cast<unsigned short *>(foo);
Results in
error C2440: 'static_cast' : cannot convert from 'wchar_t *' to 'unsigned short *' ...
On Mac OS X or Linux g++: sizeof(wchar_t) is 4
wchar_t *foo = 0;
static_cast<unsigned int *>(foo);
Results in
error: invalid static_cast from type 'wchar_t*' to type 'unsigned int*'
Of course, I can always use reinterpret_cast. However, I would like to understand why it is deemed illegal by the compiler to static_cast to the appropriate integer type. I'm sure there is a good reason...
You cannot cast between unrelated pointer types. The size of the type pointed to is irrelevant. Consider the case where the types have different alignment requirements, allowing a cast like this could generate illegal code on some processesors. It is also possible for pointers to different types to have differrent sizes. This could result in the pointer you obtain being invalid and or pointing at an entirely different location. Reinterpret_cast is one of the escape hatches you hacve if you know for your program compiler arch and os you can get away with it.
As with char, the signedness of wchar_t is not defined by the standard. Put this together with the possibility of non-2's complement integers, and for for a wchar_t value c,
*reinterpret_cast<unsigned short *>(&c)
may not equal:
static_cast<unsigned short>(c)
In the second case, on implementations where wchar_t is a sign+magnitude or 1's complement type, any negative value of c is converted to unsigned using modulo 2^N, which changes the bits. In the former case the bit pattern is picked up and used as-is (if it works at all).
Now, if the results are different, then there's no realistic way for the implementation to provide a static_cast between the pointer types. What could it do, set a flag on the unsigned short* pointer, saying "by the way, when you load from this, you have to also do a sign conversion", and then check this flag on all unsigned short loads?
That's why it's not, in general, safe to cast between pointers to distinct integer types, and I believe this unsafety is why there is no conversion via static_cast between them.
If the type you're casting to happens to be the so-called "underlying type" of wchar_t, then the resulting code would almost certainly be OK for the implementation, but would not be portable. So the standard doesn't offer a special case allowing you a static_cast just for that type, presumably because it would conceal errors in portable code. If you know reinterpret_cast is safe, then you can just use it. Admittedly, it would be nice to have a straightforward way of asserting at compile time that it is safe, but as far as the standard is concerned you should design around it, since the implementation is not required even to dereference a reinterpret_casted pointer without crashing.
By spec using of static_cast restricted by narrowable types, eg: std::ostream& to std::ofstream&. In fact wchar_t is just extension but widely used.
Your case (if you really need it) should be fixed by reinterpret_cast
By the way MSVC++ has an option - either treat wchar_t as macro (short) or as stand-alone datatype.
Pointers are not magic "no limitations, anything goes" tools.
They are, by the language specification actually very constrained. They do not allow you to bypass the type system or the rest of the C++ language, which is what you're trying to do.
You are trying to tell the compiler to "pretend that the wchar_t you stored at this address earlier is actually an int. Now read it."
That does not make sense. The object stored at that address is a wchar_t, and nothing else. You are working in a statically typed language, which means that every object has one, and juts one, type.
If you're willing to wander into implementation-defined behavior-land, you can use a reinterpret_cast to tell the compiler to just pretend it's ok, and interpret the result as it sees fit. But then the result is not specified by the standard, but by the implementation.
Without that cast, the operation is meaningless. A wchar_t is not an int or a short.