有没有一种“安全”的静态类型转换替代方案?

17

在C++11/14中,是否有“安全”的替代static_cast的方法,或者实现此功能的库?

所谓“安全”,是指该转换仅应允许不会失去精度的转换。因此,只有当一个数字适合于int32_t时,才允许从int64_tint32_t的转换,否则将报告错误。


7
您希望这是一个运行时错误吗?还是希望它在编译时作为警告/错误出现,提示可能会有数据/精度损失? - Kevin
8
好的,安全的选择是根本不进行铸造。 - Slava
2
C++ 中没有内置的功能。虽然 numeric_limits 使得将其实现为一个函数变得非常简单,该函数可以抛出异常或进行运行时断言。 - NathanOliver
2
你是否已经开启了编译器警告?在某些情况下(不是 static_cast,而是将一个更大的类型初始化或赋值给一个较小的类型),编译器会警告精度损失。 - 0x5453
12
numeric_cast<>()是Boost库中的一个模板函数,用于在不损失精度的情况下将一种数字类型转换为另一种数字类型。它可用于防止类型溢出和截断。 - Swordfish
显示剩余5条评论
4个回答

36

这里有一个gsl::narrow

narrow // narrow<T>(x) 如果 static_cast<T>(x) == x,则为static_cast<T>(x),否则会抛出narrowing_error


15
好的答案。也许值得扩展一下你的答案,以建立Guideline支持库的可信度?一些读者可能不熟悉标准C++基金会、 C++核心准则或Guideline支持库,因此可能会将“gsl”视为“仅仅是另一个库”而忽略它。 - Frank Boyne
2
@FrankBoyne同意了,一开始我也纳闷GNU科学库怎么会有C++工具... - Ruslan

24
您的用例相反了。 static_cast(以及其他C++样式的强制转换)的预期用途是指示程序员的意图。当您编写auto value = static_cast<int32_t>(value_64);时,您要表达的是:“是的,我非常*希望*在执行此赋值时将这个值向下转换,可能会截断它。”。因此,编译器(在正常情况下可能会抱怨此转换,例如如果您编写了int32_t value = value_64;)观察到“好吧,程序员告诉我这就是他们想要的;为什么他们要欺骗我呢?”,并会悄悄编译代码。
如果您希望您的C++代码在不安全的转换时发出警告或抛出错误,您需要显式不使用static_castconst_castreinterpret_cast,并让编译器完成它的工作。编译器有更改警告如何处理的标志(将int64_t向下转换为int32_t通常只会导致警告),因此,请确保使用正确的标志将警告强制视为错误。

6
我建议他们如果想要这样的指示,就不要使用这些转换。编译器通常会在发生缩小转换时发出警告,而无需显式进行转换。 - Xirema
3
编译器通常不会发出警告,而当将“long”强制转换为“char”时,编译器经常会过度警告,但开发人员知道该值始终在0和100之间。 - Mooing Duck
7
如果你知道值始终在0-100之间,那就用static_cast,因为那正是它的用途所在。 - user253751
4
我不确定你希望编译器做什么?它要么可以超出范围(这种情况下你会收到警告),要么不能超出(这种情况下请使用static_cast)。 - user253751
3
@immibis 我认为期望的行为是生成类似于 if (i < 256) static_cast<char>(i); else throw bad_cast_exception; 的代码。 - Gavin S. Yancey
显示剩余4条评论

0

假设问题是关于编译时检测潜在的有损转换...

一个简单的工具还没有在这里提到,那就是列表初始化不允许缩小,所以你可以写:

void g(int64_t n)
{
    int32_t x{n};   // error, narrowing

    int32_t g;
    g = {n};        // error, narrowing
}

NB. 有些编译器在默认模式下可能会显示“警告”并继续编译这个格式错误的代码,通常你可以通过编译标志来配置这种行为。


-1

你可以使用SFINAE创建自己的模板。以下是一个示例:

template <typename T, typename U>
typename std::enable_if<sizeof(T) >= sizeof(U),T>::type 
safe_static_cast(U&& val)
{
    return static_cast<T>(val);
}

int main()
{
    int32_t y = 2;
    std::cout << safe_static_cast<int32_t>(y) << std::endl;
    std::cout << safe_static_cast<int16_t>(y) << std::endl; // compile error
}

只有在您转换的大小>=源大小时,才会编译此内容。

请在此处尝试

您可以使用numeric_limitstype_traits来进一步复杂化此过程以处理其他类型。

请注意,我的解决方案是编译时解决方案,因为您询问了static_cast,其中static指“在编译时确定”。


6
这种测量精度的方法 a) 不好,因为例如 sizeof(float) < sizeof(int64_t),而 b) 不符合原发帖人的要求,即允许将类型转换为较小的类型,但在溢出时抛出运行时错误。 - leftaroundabout
@leftaround 这可能是一个XY问题。只有提供的信息很难提供清晰的解决方案。 - The Quantum Physicist
2
@TheQuantumPhysicist 不同意。Caleth 和 Swordfish 都已经进入了。事实上,他们的解决方案都表明这是一个已经理解和解决的问题。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit,我希望你意识到反复转换并查看结果是否改变是多么微不足道。有一百万种正确(或有意义)提问的方式,你不需要在编译时进行强制类型转换。Static_cast用于编译时,因此相关检查应在编译时完成,而不是运行时。这就是我的看法。 - The Quantum Physicist
2
实际上,来回转换验证是不够的。这种方式可能会导致有符号性差异被忽略。 - Deduplicator
显示剩余11条评论

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