Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent implicit conversion from int to unsigned int?

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.

like image 652
463035818_is_not_a_number Avatar asked Mar 22 '16 18:03

463035818_is_not_a_number


People also ask

How do you stop implicit conversions in C++?

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);

What happens when you cast an int to an unsigned int?

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.)

What types can int be implicitly converted to?

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.

Can signed variables be converted to unsigned?

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.


1 Answers

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 } 
like image 63
skypjack Avatar answered Sep 22 '22 02:09

skypjack