【问题标题】:Is there a "safe" static_cast alternative?是否有“安全”的 static_cast 替代方案?
【发布时间】:2018-10-17 14:54:27
【问题描述】:

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

我所说的“安全”是指强制转换应该只允许不丢失精度的强制转换。所以从int64_tint32_t 的转换只有在数字适合int32_t 时才被允许,否则会报告错误。

【问题讨论】:

  • 您希望这是一个运行时错误吗?还是成为可能会丢失数据/精度的编译时警告/错误?
  • 是的,安全的替代方案根本不做演员
  • C++ 没有内置任何东西。尽管numeric_limits 将其实现为一个会引发异常或执行运行时断言的函数是微不足道的。
  • 您是否打开了编译器警告?在某些情况下(不是 static_cast,而是将较大的类型初始化或分配给较小的类型)编译器会警告精度损失。

标签: c++ c++11 type-safety narrowing


【解决方案1】:

gsl::narrow

窄 // narrow<T>(x)static_cast<T>(x) 如果 static_cast<T>(x) == x 或者它抛出 narrowing_error

【讨论】:

  • 好答案。也许值得扩展您的答案以确立 Guideline 支持库 的诚意?有些读者可能不熟悉Standard C++ FoundationC++ Core Guidelines 或指南支持库,因此可能会将gsl 视为“只是另一个库”?
  • @FrankBoyne 同意,起初我想知道,为什么 GNU 科学库有 C++ 实用程序...
【解决方案2】:

你已经把用例颠倒过来了。

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 通常只会导致警告),因此请确保使用正确的标志来强制将警告视为错误。

【讨论】:

  • @Caleth 我对他们的建议是如果他们想要这样的指示,就不要使用这些演员表。编译器通常会在没有显式转换的情况下发生缩小转换时发出警告。
  • @Xirema:(A) 编译器通常不会发出警告,并且 (B) 编译器在将 long 转换为 char 时经常会过度警告,但开发人员知道该值始终介于 0 之间和 100。
  • @MooingDuck 如果您知道该值始终为 0-100,则使用 static_cast,这就是它的用途...
  • @immibis:让我纠正一下自己;开发人员知道该值应该在 0 到 100 之间,但如果确定该假设不正确,则希望抛出异常。
  • @MooingDuck 我不确定你希望编译器做什么?它可以在范围之外(在这种情况下你应该得到警告)或者它不能(在这种情况下使用 static_cast)。
【解决方案3】:

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

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

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

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

注意。一些默认模式下的编译器可能会显示“警告”并继续编译这种格式错误的代码,通常您可以通过编译标志配置此行为。

【讨论】:

    【解决方案4】:

    您可以使用 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
    }
    

    仅当您转换为的大小 >= 源大小时才会编译。

    试试here

    对于其他类型,您可以使用 numeric_limitstype_traits 进一步复杂化。

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

    【讨论】:

    • a) 是衡量精度的不好方法,例如sizeof(float) &lt; sizeof(int64_t),并且 b) 不符合 OP 的要求,即 allow 强制转换为较小的类型,但在溢出时会引发运行时错误。
    • @leftaroundabout OP没有提到什么样的错误。我看到的正确解释是在编译时。在运行时这样做是糟糕的设计!!!!!!另外,这里对精度的定义是模棱两可的,所以我就以这个为例,提到了 numeric_limits 和 type_traits 来修改它,使它符合他想要的条件。
    • @TheQuantumPhysicist 不同意。 Caleth 和 Swordfish 都加入了。事实上,他们的解决方案都表明这是一个已被理解和解决的问题。
    • @LightnessRacesinOrbit 我希望您意识到来回投射并查看结果是否没有改变是多么微不足道。有一百万种方法可以以正确的方式(或以有意义的方式,这就是为什么这是一个 XY 问题)提出这个问题,而您在编译时不涉及强制转换。 static_cast 用于编译时,因此应在编译时进行相关检查。不是运行时。我就是这么看的。
    • 其实来回投射验证是不够的Differing signedness might slip through the cracks that way.
    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 2011-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    相关资源
    最近更新 更多