首页 文章

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

提问于
浏览
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"));
   }
}

3 回答

  • 2

    让我解释一下你在这里遇到的问题 .

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

    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 是不明确的,因此是不可能的 . 对我来说,看起来你想要根据传递的第一个参数("actual value")推断出类型,并且只是尝试将边界转换为相同的类型 . 换句话说,一些不会强迫你写的东西

    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"));
       }
    }
    

    在这里,您可以看到Live Demo中的代码 .

  • 12

    您不需要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;
    }
    

相关问题