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

Как я могу принудительно ввести тип параметра шаблона?

Я приведу следующий пример, чтобы проиллюстрировать мой вопрос:

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;

Существуют ли другие решения?

4b9b3361

Ответ 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.

Ответ 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