正确地将有符号转换为无符号
Properly casting signed to unsigned
我正在使用一个 C 库,它使用无符号整数作为某些数据的索引。但有时,如果函数无法 return 索引,则函数 return 那些已签名的索引是为了 return -1
。*
如何防止 implicit conversion changes signedness
警告,并在无法进行转换时抛出运行时错误?您会建议包装库函数以使用异常进行错误处理并且仅使用 return 正确的值吗?
有没有标准的方法来做到这一点:
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
// pointless c function to demonstrate the question
// parse the string to an unsigned integer, return -1 on failure
int atoui(char const* str) {
char* pend;
long int li=strtol(str, &pend, 10);
if ( errno!=0 || *pend!='[=10=]' || li<0 || li>INT_MAX ) {
return -1;
} else {
return li;
}
}
// --8<---
#include <stdexcept>
// How to do this properly?
unsigned int unsign(int i) {
if(i<0) {
throw std::runtime_error("Tried to cast negative int to unsigned int");
} else {
return static_cast<unsigned>(i);
}
}
int main() {
unsigned int j=unsign(atoui("42")); // OK
unsigned int k=unsign(atoui("-7")); // Runtime error
}
编辑:我没有注意到您使用的是 C++,我之前的回答只假定了 C。
最简单最标准的方法是使用
std::optional<unsigned int> index;
而不是使用 -1 或其他标记值来表示无效索引。如果索引无效,你只是不设置可选的。然后你可以用
查询它
index.has_value()
判断是否有效。
如果你想在运行时进行范围检查(即允许类型之间的转换 iff 持有的值可以保持),Boost 有 numeric_cast
可以实现这一点。
如果您不想使用 Boost,您的方法看起来还不错。
标准库没有这样的函数,但是写这样的模板还是很容易的:
template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr auto unsigned_cast(Sint i)
{
if(i < 0) throw std::domain_error("Outside of domain");
return static_cast<std::make_unsigned_t<SInt>>(i);
}
如果您不喜欢为这些琐碎的事情抛出异常,您也可以 return 和 optional
:
template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr std::optional<std::make_unsigned_t<SInt>> unsigned_cast_opt(Sint i)
{
if(i < 0) return std::nullopt;
return static_cast<std::make_unsigned_t<SInt>>(i);
}
我正在使用一个 C 库,它使用无符号整数作为某些数据的索引。但有时,如果函数无法 return 索引,则函数 return 那些已签名的索引是为了 return -1
。*
如何防止 implicit conversion changes signedness
警告,并在无法进行转换时抛出运行时错误?您会建议包装库函数以使用异常进行错误处理并且仅使用 return 正确的值吗?
有没有标准的方法来做到这一点:
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
// pointless c function to demonstrate the question
// parse the string to an unsigned integer, return -1 on failure
int atoui(char const* str) {
char* pend;
long int li=strtol(str, &pend, 10);
if ( errno!=0 || *pend!='[=10=]' || li<0 || li>INT_MAX ) {
return -1;
} else {
return li;
}
}
// --8<---
#include <stdexcept>
// How to do this properly?
unsigned int unsign(int i) {
if(i<0) {
throw std::runtime_error("Tried to cast negative int to unsigned int");
} else {
return static_cast<unsigned>(i);
}
}
int main() {
unsigned int j=unsign(atoui("42")); // OK
unsigned int k=unsign(atoui("-7")); // Runtime error
}
编辑:我没有注意到您使用的是 C++,我之前的回答只假定了 C。
最简单最标准的方法是使用
std::optional<unsigned int> index;
而不是使用 -1 或其他标记值来表示无效索引。如果索引无效,你只是不设置可选的。然后你可以用
查询它index.has_value()
判断是否有效。
如果你想在运行时进行范围检查(即允许类型之间的转换 iff 持有的值可以保持),Boost 有 numeric_cast
可以实现这一点。
如果您不想使用 Boost,您的方法看起来还不错。
标准库没有这样的函数,但是写这样的模板还是很容易的:
template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr auto unsigned_cast(Sint i)
{
if(i < 0) throw std::domain_error("Outside of domain");
return static_cast<std::make_unsigned_t<SInt>>(i);
}
如果您不喜欢为这些琐碎的事情抛出异常,您也可以 return 和 optional
:
template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr std::optional<std::make_unsigned_t<SInt>> unsigned_cast_opt(Sint i)
{
if(i < 0) return std::nullopt;
return static_cast<std::make_unsigned_t<SInt>>(i);
}