【问题标题】:Using boost::numeric_cast<>使用 boost::numeric_cast<>
【发布时间】:2010-12-12 21:34:55
【问题描述】:

当我想在不同的整数类型之间进行转换时,似乎最好的语法是使用boost::numeric_cast&lt;&gt;():

int y = 99999;
short x = boost::numeric_cast<short>(y); // will throw an exception if y is too large

我从来没有用过;但是语法非常简单,所以一切都很好。

现在假设我想做一些更高级的事情:我希望它返回目标类型的最小值或最大值(饱和度),而不是抛出异常。我想不出一种表达方式,但documentation 表明这是可能的(可能使用RawConverter 策略)。我能想到的只是以下丑陋的:

short x = numeric_cast<short>(max(min(y, SHORT_MAX), SHORT_MIN);

那么如何使用 boost 的 numeric_cast 来表达“饱和演员表”?

【问题讨论】:

    标签: c++ boost type-conversion


    【解决方案1】:

    你可能会这样做:

    #include <limits>
    
    template<typename Target, typename Source>
    Target saturation_cast(Source src) {
       try {
          return boost::numeric_cast<Target>(src);
       }
       catch (const boost::negative_overflow &e) {
          return std::numeric_limits<Target>::lowest();
          /* Or, before C++11:
          if (std::numeric_limits<Target>::is_integer)
             return std::numeric_limits<Target>::min();
          else
             return -std::numeric_limits<Target>::max();
          */
       }
       catch (const boost::positive_overflow &e) {
          return std::numeric_limits<Target>::max();
       }
    }
    

    (对于支持它的类型,错误情况也可能返回 -inf/+inf)。

    这样您就可以让 Boost 的 numeric_cast 确定该值是否超出范围,然后可以做出相应的反应。

    【讨论】:

    • @Kos:如果一个项目没有使用所有的语言特性,那就是那个项目的问题。没有人应该为不使用某些功能的语言编写代码,因为其他人可能不喜欢该功能。
    • @Kos:假设 boosts numeric_cast 处理有符号/无符号(我当然希望如此),这仍然可以正确处理。如果你不想使用任何异常,你可能首先不应该使用numeric_cast,因为该函数的重点似乎是确实抛出错误。
    • @sth,OP 显然想要一个永远不会抛出异常(而是使值饱和)的解决方案。如果我们可以选择在有或没有例外的情况下进行内部实现,那么在不必要的情况下最好避免使用它们(出于上述原因)——仅此而已:)。
    • @Kos:不,编码标准是关于您喜欢如何放置括号以及您喜欢如何记录代码、空格或制表符。删除语言特性,绝对不是我们任何人都应该遵守的编码标准。
    • @DeadMG:实际上,如果你读过 Sutter 的 C++ 编码标准,你应该意识到真正的编码标准不是关于放置括号(那些是命名约定或格式标准) .编码标准是关于强制执行良好实践并引导开发人员走上更好的道路。
    【解决方案2】:

    嗯...如果上述方法有效,一般的解决方案可能是:

    template<typename TypeFrom, typename TypeTo>
    TypeTo saturated_cast(TypeFrom value) {
        TypeTo valueMin = std::numeric_limits<TypeTo>::min();
        TypeTo valueMax = std::numeric_limits<TypeTo>::max();
        return boost::numeric_cast<TypeTo>( std::max(std::min(value,valueMax),valueMin) );
    }
    

    希望我做对了...无论如何,你已经掌握了这个概念 :)

    ....顺便说一句:我认为您可以在这里改用static_cast,因为在执行限制后您不能再超出范围,因此您不需要额外检查numeric_cast。

    【讨论】:

    • 与 GregHill 相同的问题,应在比较之前进行转换,否则使用 numeric_cast 没有任何好处,因为无论如何它都会执行相同的比较以检查范围是否正确.此外,std::min 和 std::max 只能为相同类型的两个参数调用,此代码将无法编译。
    【解决方案3】:

    如果您对 C++17 没问题,但不希望您的转换算法在内部引发异常,您可以使用 std::clamp 和一些包装来处理越界值。

    template <typename TTo, typename TFrom>
    constexpr TTo clamp_cast(const TFrom& src) noexcept
    {
        using to_limits   = std::numeric_limits<TTo>;
        using larger_type = std::conditional_t<(sizeof(TFrom) < sizeof(TTo)), TTo, TFrom>;
    
        if constexpr (std::is_same_v<TTo, TFrom>)
        {
            // don't bother if it is the same type
            return src;
        }
        else if constexpr (std::is_unsigned_v<TFrom>)
        {
            // if source is unsigned, we only need to worry about upper bound
            return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
        }
        else if constexpr (std::is_unsigned_v<TTo>)
        {
            // source is signed, but destination is not
            if (src < TFrom(0))
                return TTo(0);
            else
                return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
        }
        else
        {
            // both are signed -- use regular clamping rules
            return TTo(std::clamp(larger_type(src),
                                  larger_type(to_limits::min()),
                                  larger_type(to_limits::max())
                                 )
                      );
        }
    }
    

    使用基本上是你所期望的:

    static_assert(uint16_t(213)   == clamp_cast<uint16_t>(213));
    static_assert(uint16_t(65535) == clamp_cast<uint16_t>(9872431));
    static_assert(uint16_t(0)     == clamp_cast<uint16_t>(-98721));
    

    【讨论】:

      猜你喜欢
      • 2018-09-14
      • 1970-01-01
      • 1970-01-01
      • 2016-03-16
      • 2012-10-29
      • 2014-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多