Как я могу принудительно ввести тип параметра шаблона?
Я приведу следующий пример, чтобы проиллюстрировать мой вопрос:
template<typename T>
T diff(T a, T b)
{
return a-b;
}
Я ожидаю, что эта функция шаблона будет работать только тогда, когда будет подписан тип T. Единственное решение, которое я могу выяснить, это использовать ключевое слово delete для всех неподписанных типов:
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
Существуют ли другие решения?
Ответы
Ответ 1
Вы можете использовать std::is_signed
вместе с std::enable_if
:
template<typename T>
T diff(T a, T b);
template<typename T>
std::enable_if_t<std::is_signed<T>::value, T> diff(T a, T b) {
return a - b;
}
Здесь std::is_signed<T>::value
есть true
тогда и только тогда, когда T
подписана (BTW, это также true
для типов с плавающей запятой, если вам это не нужно, рассмотрите объединение с std::is_integral
).
std::enable_if_t<Test, Type>
совпадает с std::enable_if<Test, Type>::type
. std::enable_if<Test, Type>
определяется как пустая структура в случае, если Test
является ложным и как структура с единственным typedef type
, равным параметру шаблона type
в противном случае.
Итак, для подписанных типов std::enable_if_t<std::is_signed<T>::value, T>
равен T
, тогда как для unsigned он не определен, а в компиляторе используется правило SFINAE, поэтому, если вам нужно указать реализацию для определенного не подписанного типа, вы можете легко сделать это:
template<>
unsigned diff(unsigned, unsigned)
{
return 0u;
}
Некоторые релевантные ссылки: enable_if, is_signed.
Ответ 2
Как насчет static assert
с std::is_signed
template<typename T>
T diff(T a, T b)
{
static_assert(std::is_signed<T>::value, "signed values only");
return a-b;
}
Посмотри туда вживую:
http://ideone.com/l8nWYQ
Ответ 3
Я бы использовал static_assert
с хорошим сообщением об ошибке. enable_if
приведет к сбою только вашей IDE и не скомпилируется с сообщением типа
идентификатор diff
не найден
что мало помогает.
Так почему бы не так:
#include <type_traits>
template <typename T>
T diff(T a, T b)
{
static_assert(std::is_signed< T >::value, "T should be signed");
return a - b;
}
таким образом, когда вы вызываете diff
с чем-то еще чем подписанным типом, вы получите компилятор для написания такого сообщения:
ошибка: T должен быть подписан
с указанием местоположения и значений на вызов diff
и что именно вы ищете.
Ответ 4
В качестве другого варианта вы, вероятно, можете добавить static_assert с типом std:: is_signed:
template<typename T>
auto diff(T x, T y)
{
static_assert(std::is_signed<T>::value, "Does not work for unsigned");
return x - y;
}
Итак, чтобы:
auto x = diff(4, 2); // works
auto x = diff(4U, 2U); // does not work
Ответ 5
Итак, у меня есть несколько проблем с вашей функцией.
Во-первых, для вашей функции требуются все 3 типа - левый, правый и результирующий типы. Поэтому signed char a; int b; diff(a-b);
не будет работать без уважительной причины.
template<class L, class R>
auto diff( L l, R r )
-> typename std::enable_if<
std::is_signed<L>::value && std::is_signed<R>::value,
typename std::decay<decltype( l-r )>::type
>::type
{
return l-r;
}
вторая вещь, которую я хочу сделать, это сделать объект diff; вы не можете легко передать свою функцию diff
, а функции более высокого порядка - потрясающие.
struct diff_t {
template<class L, class R>
auto operator()(L l, R r)const
-> decltype( diff(l,r) )
{ return diff(l,r); }
};
Теперь мы можем передать diff_t{}
в алгоритм, поскольку он содержит "набор перегрузки" diff
в одном (тривиальном) объекте С++.
Теперь это серьезный перебор. Простой static_assert
также может работать.
static_assert
будет генерировать лучшие сообщения об ошибках, но не будет поддерживать другой код с помощью SFINAE, чтобы узнать, можно ли вызывать diff
. Это просто приведет к жесткой ошибке.
Ответ 6
Что ожидает ваша программа в результате? Как бы то ни было, вы возвращаете без знака в результате разницы. ИМХО, это ошибка, ожидающая того, что произойдет.
#include <type_trait>
template<typename T>
auto diff(T&& a, T&& b)
{
static_assert (std::is_unsigned<T>::value);
return typename std::make_signed<T>::type(a - b);
}
Более современное ожидание, чтобы написать это:
inline auto diff(const auto a, const auto b)
{
static_assert ( std::is_unsigned<decltype(a)>::value
&& std::is_unsigned<decltype(b)>::value );
return typename std::make_signed<decltype(a -b)>::type(a - b);
}
[edit] Я чувствую необходимость добавить этот комментарий: использование неподписанных интегральных типов в математических уравнениях всегда сложно. Приведенный выше пример будет очень полезным дополнением к любому математическому пакету, если в реальных ситуациях вам часто приходится прибегать к кастингу, чтобы сделать результат различий signed
, или математика не работает.
Ответ 7
#include <type_traits>
template<typename T>
std::enable_if_t<(0>-T(1)),T> diff(T a, T b)
{
return a-b;
}
use (0 > -T (1)), я полагаю, что init T для -1 будет меньше нуля, а значение без знака не может быть меньше 0