如何强制模板参数类型进行签名?

时间:2017-06-15 08:22:51

标签: c++ c++11 templates c++14

我将使用以下示例来说明我的问题:

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;

还有其他解决方案吗?

7 个答案:

答案 0 :(得分:45)

您可以将std::is_signedstd::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当且仅当true已签名(BTW,对于浮点类型,它也是std::is_integral,如果您不这样做需要它,考虑与std::enable_if_t<Test, Type>)结合使用。

std::enable_if<Test, Type>::typestd::enable_if<Test, Type>相同。如果Test为false,则type被定义为空结构,否则定义为仅具有typedef Type等于模板参数std::enable_if_t<std::is_signed<T>::value, T>的结构。

因此,对于签名类型,T等于template<> unsigned diff(unsigned, unsigned) { return 0u; } ,而对于无符号,它没有定义,编译器使用SFINAE规则,因此,如果需要为a指定实现特别是非签名类型,你可以轻松地做到这一点:

/search/

一些相关链接:enable_ifis_signed

答案 1 :(得分:35)

static assertstd::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

答案 2 :(得分:11)

我会使用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的位置和值,这正是您要查找的内容。

答案 3 :(得分:7)

作为另一种选择,您可能可以使用 std :: is_signed 类型特征添加 static_assert

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

答案 4 :(得分:2)

因此,我的功能存在一些问题。

首先,您的函数需要匹配所有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函数,而高阶函数也很棒。

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{}传递给算法,因为它拥有&#34;重载集&#34;在一个(普通的)C ++对象中的diff

现在这是严重的矫枉过正。一个简单的static_assert也可以。

static_assert会生成更好的错误消息,但不会使用SFINAE支持其他代码,以查看是否可以调用diff。它只会产生一个硬错误。

答案 5 :(得分:2)

您的计划对结果有何期待?就目前而言,由于存在差异,您将返回无符号。恕我直言,这是一个等待发生的错误。

#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,或者数学不起作用。

答案 6 :(得分:-1)

#include <type_traits>
template<typename T>
std::enable_if_t<(0>-T(1)),T> diff(T a, T b)
{
  return a-b;
}

使用(0&gt; -T(1)),我假设init T为-1将小于0.而无符号值不可能小于0