适当地将有符号数转换为无符号数

5

我正在使用一个使用无符号整数作为某些数据索引的C库。但有时,函数会将这些索引作为带符号的返回,以便在函数无法返回索引时返回-1

我该如何避免implicit conversion changes signedness警告,并在转换不可能时抛出运行时错误?您是否建议封装库函数以使用异常进行错误处理并仅返回正确的值? 是否有一种标准的方法来实现这个:

#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!='\0' || 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
}
3个回答

6
标准库中没有这样的函数,但编写此类模板非常容易:
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);
}

你也可以返回一个可选值(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);
}

非常感谢。我找不到is_integral_v的任何文档,只有is_integral的文档,这些头文件在哪里? - Gamification
@Gamification 它在 std::is_integral 文档 中:template< class T > inline constexpr bool is_integral_v = is_integral<T>::value; - phuclv

5
如果您想在运行时进行范围检查(即仅在保持值的情况下允许类型转换),Boost提供了numeric_cast来实现此功能。
如果您不想使用Boost,您的方法看起来足够好。

0

编辑:我忽略了您在使用C++,之前的回答是针对C的。

最简单和标准的方法是使用

std::optional<unsigned int> index;

不要使用 -1 或其他标记值来表示无效索引。如果索引无效,只需不设置可选项即可。然后,您可以查询它的状态。

index.has_value()

为了确定它是否有效。


我正在从C++中使用一个C函数,实际上,它在出错时返回-1。很抱歉我的问题没有表述得更清楚。 - Gamification
你可以轻松地编写一个包装器,将其转换为std::optional。如果函数返回-1,则不设置可选项,否则将其设置为返回的值。 - H. Al-Amri

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