正确地将有符号转换为无符号

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