Say I have a class with an integer that should be always positive with a simple C'tor:
class A { unsigned int x; public: A(unsigned int X) : x(X) {} };
And lets say someone accidentally creates an A object with the value -5
. Of course, this is not valid and X
will get a really huge value since the MSB now doesnt stand for the number sign. Problem is, that now I can't really check if the number is negative or invalid (maybe bitwise?).
Should I just avoid the unsigned
and just use a plain int
? By doing so, i could just throw an exception if the given value is beyond a max value OR below zero.
Would love to hear some suggestions.
Check for an equivalent negative int value only requires checking the highest bit, if it is 1 , then that unsigned number would be represented as a negative int value -- but you would have to further check that the value does not exceed INT_MIN before proceeding with any such conversion, otherwise you need to extend to ...
The advantage to using the unsigned version (when you know the values contained will be non-negative) is that sometimes the computer will spot errors for you (the program will "crash" when a negative value is assigned to the variable). We will talk more about assigning and using uints later in class.
Unsigned is good programming practice to indicate the intention, to yourself and others, of use of the data element - just in the same way all types are used. For example, a normal array index variable should never be negative and so declaring the variable unsigned should be best practice.
An n-bit unsigned variable has a range of 0 to (2n)-1. When no negative numbers are required, unsigned integers are well-suited for networking and systems with little memory, because unsigned integers can store more positive numbers without taking up extra memory.
Two approaches come to mind:
Add an explicit conversion for the 'signed' types.
#include <cassert> class A { unsigned int x; public: A(unsigned int X) : x(X) {} explicit A(int X) : x(static_cast<unsigned>(X)) { assert(X>=0); // note X, not x! } }; int main() { A ok(5); A bad(-5); }
Prohibit implicit conversions by deleting better overloads:
A(int X) = delete; A(long X) = delete; A(char X) = delete;
This will require all users to cast to unsigned before constructing the A instance. It's safe but clumsy.
Note that this does not prohibit implicit conversions from all integral types (e.g. enum
s) so you'd need to do more to make this fool proof.
Here is a rudimentary SFINAE-based example that accepts all implicit conversions except if they involve signed values: Live on Coliru
#include <type_traits> #include <limits> class A { unsigned int x; public: template<typename T, typename = typename std::enable_if<std::is_integral<T>::value, void>::type> A(T X) : x(X) { static_assert(!std::numeric_limits<T>::is_signed, "Signed types cannot be accepted"); } }; int main() { A ok(5u); A bad(-5); }
And lets say someone accidentally creates an A object with the value -5
While it may be good practice to make your program robust enough to accept such errors, the root cause of the bug is a sloppy programmer who does not have sufficient warnings enabled.
To get to the bottom of the problem, you need to ensure that the code is compiled with all warnings enabled, and perhaps also consider using an external static analysis tool.
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