Как предотвратить неявное преобразование из 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 отрицательно, но было бы лучше, если бы я мог получить ошибку компилятора для этого.

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

Я заинтересован как в C ++ 11, так и в решениях до C ++ 11, предпочтительно в обоих.

30

Решение

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

Это следует (не работает, как требуется) пример:

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)) где это возможно.

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

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

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

Я хотел бы добавить еще одно решение, событие, хотя оно действительно с C ++ 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
}
29

Другие решения

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

Foo(int x) = delete;
27

Если вы хотите получить предупреждение о каждый появление такого кода, и вы используете 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,

8
По вопросам рекламы ammmcru@yandex.ru