Suppose you have this:
struct Foo { Foo(unsigned int x) : x(x) {} unsigned int x; }; int main() { Foo f = Foo(-1); // how to get a compiler error here? std::cout << f.x << std::endl; }
Is it possible to prevent the implicit conversion?
The only way I could think of is to explicilty provide a constructor that takes an int
and generates some kind of runtime error if the int
is negative, but it would be nicer if I could get a compiler error for this.
I am almost sure, that there is a duplicate, but the closest I could find is this question which rather asks why the implicit conversion is allowed.
I am interested in both, C++11 and pre C++11 solutions, preferably one that would work in both.
One of the most interesting features of C++ is to implicitly convert the argument of a called function to its formal parameter type. As an example consider: void Foo(double param);
You can convert an int to an unsigned int . The conversion is valid and well-defined. Since the value is negative, UINT_MAX + 1 is added to it so that the value is a valid unsigned quantity. (Technically, 2N is added to it, where N is the number of bits used to represent the unsigned type.)
Any integral numeric type is implicitly convertible to any floating-point numeric type. There are no implicit conversions to the byte and sbyte types. There are no implicit conversions from the double and decimal types. There are no implicit conversions between the decimal type and the float or double types.
To convert a signed integer to an unsigned integer, or to convert an unsigned integer to a signed integer you need only use a cast. For example: int a = 6; unsigned int b; int c; b = (unsigned int)a; c = (int)b; Actually in many cases you can dispense with the cast.
Uniform initialization prevents narrowing.
It follows a (not working, as requested) example:
struct Foo { explicit Foo(unsigned int x) : x(x) {} unsigned int x; }; int main() { Foo f = Foo{-1}; std::cout << f.x << std::endl; }
Simply get used to using the uniform initialization (Foo{-1}
instead of Foo(-1)
) wherever possible.
EDIT
As an alternative, as requested by the OP in the comments, a solution that works also with C++98 is to declare as private
the constructors getting an int
(long int
, and so on).
No need actually to define them.
Please, note that = delete
would be also a good solution, as suggested in another answer, but that one too is since C++11.
EDIT 2
I'd like to add one more solution, event though it's valid since C++11.
The idea is based on the suggestion of Voo (see the comments of Brian's response for further details), and uses SFINAE on constructor's arguments.
It follows a minimal, working example:
#include<type_traits> struct S { template<class T, typename = typename std::enable_if<std::is_unsigned<T>::value>::type> S(T t) { } }; int main() { S s1{42u}; // S s2{42}; // this doesn't work // S s3{-1}; // this doesn't work }
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