Подтвердить что ты не робот

Как предотвратить неявное преобразование из int в unsigned int?

Предположим, что у вас есть это:

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

Можно ли предотвратить неявное преобразование?

Единственный способ, которым я мог подумать, - это объяснить конструктор, который принимает int и генерирует некоторую ошибку выполнения, если int отрицательный, но было бы лучше, если бы я мог получить ошибку компилятора для это.

Я почти уверен, что есть дубликат, но ближайший я мог бы найти этот вопрос, который скорее спрашивает, почему допускается неявное преобразование.

Меня интересуют как решения С++ 11, так и pre С++ 11, предпочтительно те, которые будут работать в обоих.

4b9b3361

Ответ 1

Унифицированная инициализация предотвращает сужение.

Это следует за (не работает, по запросу):

struct Foo {
    explicit Foo(unsigned int x) : x(x) {}
    unsigned int x;
};

int main() {
    Foo f = Foo{-1};
    std::cout << f.x << std::endl;
}

По возможности используйте обычную инициализацию (Foo{-1} вместо Foo(-1)).

ИЗМЕНИТЬ

В качестве альтернативы, как запрошено OP в комментариях, решение, которое работает также с С++ 98, заключается в объявлении private конструкторов, получающих int (long int и т.д.).
Нет необходимости определять их.
Пожалуйста, обратите внимание, что = delete также будет хорошим решением, как это предлагается в другом ответе, но это тоже происходит с С++ 11.

РЕДАКТИРОВАТЬ 2

Я хотел бы добавить еще одно решение, событие, хотя оно действительно с С++ 11.
Идея основана на предположении Voo (см. Комментарии ответа Брайана для дальнейших подробностей) и использует SFINAE для аргументов конструктора.
Это следует за минимальным рабочим примером:

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

Ответ 2

Вы можете принудительно выполнить компиляцию, удалив нежелательную перегрузку.

Foo(int x) = delete;

Ответ 3

Если вы хотите, чтобы вас предупреждали о каждом появлении такого кода, и вы используете GCC, используйте параметр -Wsign-conversion.

foo.cc: In function ‘int main()’:
foo.cc:8:19: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]
     Foo f = Foo(-1);     // how to get a compiler error here?
                   ^

Если вы хотите получить ошибку, используйте -Werror=sign-conversion.