Запретить преобразование uint64_t в uint16_t
Почему следующий код компилируется в clang++?
Есть ли какие-либо флагов С++, чтобы это не происходило - я хотел бы, чтобы компилятор выдал ошибку, потому что я передаю std:: uint64_t в качестве аргумента функции, принимающей std:: uint16_t.
#include <cstdint>
using namespace std;
void foo(uint16_t x) {
}
int main() {
uint64_t x = 10000;
foo(x);
return 0;
}
Ответы
Ответ 1
вы можете удалить функцию в С++ 11
void foo(uint64_t) = delete;
он работает, добавляя подпись при разрешении перегрузки функции, и если это было лучшее совпадение, возникает ошибка. Вы также можете сделать его общим, чтобы разрешить только оригинальную подпись
template <class T> void foo( T&& ) = delete;
Ответ 2
Вы также можете использовать enable_if
как возвращаемый параметр SFINAE
#include <iostream>
#include <cstdint>
#include <type_traits>
template<typename T>
typename std::enable_if<std::is_same<T, uint16_t>::value>::type
foo(T x)
{
std::cout << "uint16_t" << std::endl;
}
template<typename T>
typename std::enable_if<!std::is_same<T, uint16_t>::value>::type
foo(T x)
{
std::cout << "rest" << std::endl;
}
int main() {
uint16_t x = 10000;
uint64_t y = 100000;
foo(x); // picks up uint16_t version
foo(y); // picks up anything else, but NOT uint16_t
return 0;
}
Таким образом, вы можете иметь одну перегрузку, которая имеет дело с uint16_t
, и другую перегрузку, которая имеет дело с чем-либо еще.
Ответ 3
Здесь решение, которое позволит расширить преобразования и предотвратить сужение:
#include <cstdint>
#include <type_traits>
void foo(uint16_t x) {
}
template <class T>
typename std::enable_if<sizeof(uint16_t) < sizeof(T)>::type foo(const T& t) = delete;
int main() {
uint64_t x = 10000;
uint16_t y = 10000;
uint8_t z = 100;
// foo(x); // ERROR: narrowing conversion
foo(y); // OK: no conversion
foo(z); // OK: widening conversion
return 0;
}
Если вы также хотите запретить вызовы с аргументами подписанных типов (конверсии между подписанными и неподписанными типами не являются "без потерь" ), вы можете использовать следующее выражение:
#include <cstdint>
#include <type_traits>
void foo(uint16_t x) {
}
template <class T>
typename std::enable_if<(sizeof(uint16_t) < sizeof(T)) ||
(std::is_signed<T>::value != std::is_signed<uint16_t>::value)
>::type
foo(const T& t) = delete;
int main() {
uint64_t u64 = 10000;
uint16_t u16 = 10000;
uint8_t u8 = 100;
int64_t s64 = 10000;
int16_t s16 = 10000;
int8_t s8 = 100;
//foo(u64); // ERROR: narrowing conversion
foo(u16); // OK: no conversion
foo(u8); // OK: widening conversion
//foo(s64); // ERROR: narrowing conversion AND signed/unsigned mismatch
//foo(s16); // ERROR: signed/unsigned mismatch
//foo(s8); // ERROR: signed/unsigned mismatch
return 0;
}
Ответ 4
Если вы хотите разрешить расширение конверсий, но запретите сужение конверсий, возможно:
void foo(uint16_t x) {
}
template <class T>
void foo( const T&& t )
{
return foo(uint16_t{t});
}
Это заставляет все типы, кроме uint16_t
самостоятельно проходить через инициализацию списка, что запрещает сужение конверсий.
Это не работает так хорошо, если у вас уже есть несколько перегрузок.
Ответ 5
Хотя большинство ответов здесь технически корректны, вы, скорее всего, не захотите, чтобы поведение применялось только к этой функции, поэтому решение "уровень кода", которое вы должны написать для каждого из этих случаев преобразования, вероятно, не то, что вы хотите.
На уровне "project/compilation" вы можете добавить этот флаг, чтобы предупредить вас об этих преобразованиях:
-Wconversion
или если вы предпочитаете прямо относиться к ним как к ошибкам:
-Werror=conversion