优雅的方法摆脱有符号和无符号整数表达式之间的比较

9

我有以下模板:

一个用于未签名的,另一个用于已签名的。是否有一种优雅的方法可以消除编译器警告而不是抑制它?

 warning: comparison between signed and unsigned integer expressions

我需要为每种类型编写函数,例如uint8、uint16等吗?

template<typename X,typename Y,typename Z, typename std::enable_if<std::is_unsigned<X>::value, bool>::type = true >
void debugValidateParameter( X aValueToCheck, Y aLowerLimit, Z aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n", aValueToCheck, aLowerLimit, aUpperLimit );
    throw(std::out_of_range("Invalid Range"));
   }
}

template<typename X,typename Y,typename Z, typename std::enable_if<std::is_signed<X>::value, bool>::type = true >
void debugValidateParameter( X aValueToCheck, Y aLowerLimit, Z aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n", aValueToCheck, aLowerLimit, aUpperLimit );
    throw(std::out_of_range("Invalid Range"));
   }
}

1
你为什么有X、Y和Z?所有的值不都应该是同一种类型吗? - JimmyB
将有符号数与无符号数进行比较就像是在比较苹果和橙子。 - Richard Hodges
3个回答

12

让我简要解释一下您的错误所在。

对我来说,看起来您通常希望对所有三个参数使用相同的类型。最直接的解决方案是使用以下定义:

template<typename X>
void debugValidateParameter( X aValueToCheck, X aLowerLimit, X aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n", aValueToCheck, aLowerLimit, aUpperLimit );
    throw(std::out_of_range("Invalid Range"));
   }
}

如果你使用一个无符号变量和两个整型字面值调用该函数,例如:
debugValidateParameter(someUnsignedInteger, 0, 100);

如果不指定类型,你将会收到一个错误 -- 为了避免这种情况,所有类型为X的参数都需要传入相同类型的值。 因此,推断类型X是不明确的,也是不可能的。看起来你想要根据传递的第一个参数(“实际值”)来推断类型,并尝试将边界转换为该相同类型。换句话说,就是不强制你编写

debugValidateParameter(someUnsignedInteger, 0u, 100u);

这可以通过为第二个和第三个参数指定它们的类型为identity_t<X>而不是只使用X,从而禁用类型推导来实现,其中identity_t的定义如下:

template<typename T>
struct identity { typedef T type; };

template<typename T>
using identity_t = typename identity<T>::type;

所以你的函数定义应该如下:
template<typename X>
void debugValidateParameter( X aValueToCheck, identity_t<X> aLowerLimit, identity_t<X> aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n", aValueToCheck, aLowerLimit, aUpperLimit );
    throw(std::out_of_range("Invalid Range"));
   }
}

在这里,您可以查看实时演示中的代码。


2
你不需要使用SFINAE或专门化,你只需要确保XYZ具有相同的符号。因此,你可以使用:
template<typename T>
void debugValidateParameter(T aValueToCheck, T aLowerLimit, T aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n",
        aValueToCheck, aLowerLimit, aUpperLimit);
    throw(std::out_of_range("Invalid Range"));
   }
}

但这需要所有参数被推导为相同的类型。

为了避免这种情况,您可以强制某些参数不被推导:

template <typename T> struct non_deducible { using type = T; };
template <typename T> using non_deducible_t = typename non_deducible<T>::type;

template<typename T>
void debugValidateParameter(T aValueToCheck,
                            non_deducible_t<T> aLowerLimit,
                            non_deducible_t<T> aUpperLimit)
{
   if( (aValueToCheck > aUpperLimit) || (aValueToCheck < aLowerLimit) )
   {
    log("ERROR: ValidateParameters, aValueToCheck = % , aLowerLimit= % , aUpperLimit= %  \n",
        aValueToCheck, aLowerLimit, aUpperLimit);
    throw(std::out_of_range("Invalid Range"));
   }
}

1
这样的东西怎么样?
#include <iostream>
#include <type_traits>
#include <stdexcept>

template <typename T1, typename T2>
bool integral_less_than(T1 t1, T2 t2)
{
  static_assert(std::is_integral<T1>::value, "");
  static_assert(std::is_integral<T2>::value, "");
  // Handle different signedness.
  if (std::is_unsigned<T1>::value)
  {
    if (!std::is_unsigned<T2>::value)
      return (t2 < 0) ? false : t1 < static_cast<typename std::make_unsigned<T2>::type>(t2);
  }
  else
  {
    if (std::is_unsigned<T2>::value)
      return (t1 < 0) ? true : static_cast<typename std::make_unsigned<T1>::type>(t1) < t2;
  }
  // Handle same signedness.
  return t1 < t2;
}

template <typename X, typename Y, typename Z>
void ValidateParameter(X aValueToCheck, Y aLowerLimit, Z aUpperLimit)
{
  if (integral_less_than(aUpperLimit, aValueToCheck) ||
      integral_less_than(aValueToCheck, aLowerLimit))
  {
    std::cout
      << "ERROR: ValidateParameter():"
      << " aValueToCheck=" << aValueToCheck
      << ", aLowerLimit=" << aLowerLimit
      << ", aUpperLimit=" << aUpperLimit
      << "\n";
//    throw(std::out_of_range("Invalid Range"));
  }
}

int main()
{
  ValidateParameter(0, -1, 1);
  ValidateParameter(0u, -1, 1);
  ValidateParameter(0, -1, 1u);
  ValidateParameter(0u, -1, 1u);
  ValidateParameter(-1, -1, 1);
  ValidateParameter(-1, -1, 1u);
  ValidateParameter(1, -1, 1);
  ValidateParameter(1u, -1, 1);
  ValidateParameter(1, -1, 1u);
  ValidateParameter(1u, -1, 1u);
  ValidateParameter(-2, -1, 1);
  ValidateParameter(-2, -1, 1u);
  ValidateParameter(2, -1, 1);
  ValidateParameter(2u, -1, 1);
  ValidateParameter(2, -1, 1u);
  ValidateParameter(2u, -1, 1u);
  return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接