Search code examples
c++implicit-conversionunsigned-integer

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.


Solution

  • 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
    }