消除有符号和无符号整数表达式之间比较的优雅方式
Elegant way to get rid of comparison between signed and unsigned integer expressions
我有以下模板:
一个用于未签名,另一个用于已签名。有没有什么优雅的方法可以在不抑制编译器警告的情况下消除它?
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"));
}
}
让我来解释一下您在这里做错了什么。
对我来说,看起来您通常希望对所有三个参数使用相同的类型。最直接的解决方案是这个定义:
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"));
}
}
您可以在实时演示中查看代码。
你不需要SFINAE或专业化,你只需要X
、Y
、Z
有相同的符号。所以你可以使用
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"));
}
}
这样的东西怎么样?
#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;
}
相关文章:
- 用于基于成员字段或函数创建比较器的快捷方式
- 内置用于比较两个(或多个)值的方式
- 重载 == 以递归方式比较两个链表
- STL映射比较器能否以某种方式获取指向映射本身的指针?
- C++中的结构是否按顺序分配内存?每次都以某种方式获得指针比较的正确答案
- ARM NEON aarch64:如何以优化的方式比较和更新 neon 寄存器
- 为什么这个简短的比较没有优化我预期的方式?
- 在 C++11 中编译时以词典方式比较两个字符串
- 消除有符号和无符号整数表达式之间比较的优雅方式
- 以最有效的方式比较两个向量
- C++等于(==)重载,比较所有属性的快捷方式或最佳方法
- 以格式化的方式打开一个具有日期的文件,并获取该日期进行比较
- 比较sha1哈希是否相等的最佳方式
- 二进制比较操作的优雅方式
- 比较输入值和从文件中读取值的最佳方式
- 比较数组的最佳方式
- C++ 字符串比较运算符>根据两个字符串的比较方式生成不同的输出
- 对象列表,按键(其成员之一)排序的最快方式(复杂度比较)
- 以本地化安全的方式比较C++中的字符串/字符和字符串/字符文本
- 在映射中使用reference-to-lambda作为比较器(正确的方式?)