5

我正在尝试提出一个可以优雅地忽略丢失数据的缩小范围(一种通用解决方案)。在 Visual Studio 中,丢失数据的缩小转换会触发“运行时检查失败 #1”。我不想关闭它,而是尝试实现一个narrow_cast可以优雅地缩小演员表并且不会触发运行时检查的方法。

Visual Studio 建议:

char c = (i & 0xFF);

所以我从这个开始,想出了这个丑陋的解决方案:

template< typename T >
struct narrow_mask
{
  static const T value = T(0xFFFFFFFFFFFFFFFF);
};

template <typename T, typename U>
T narrow_cast(const U& a)
{
  return static_cast<T>(a & narrow_mask<T>::value );
}

虽然它可以工作(VS 丢失常量数据似乎很好),但它既不完整(不支持非整数数据)也不正确(我认为它对于有符号值不能正常工作)。

对于更好的解决方案或更好的narrow_mask 实施有什么建议吗?

编辑:面对这个问题是 VS 特定的评论,我检查了标准文档,似乎缩小的结果static_cast取决于实现。因此,这个问题可以更好地表述为创建一个定义明确的(ergo,不依赖于实现)缩小范围。我不太关心结果值的细节,只要它定义明确并且依赖于类型(不是return 0)。

4

4 回答 4

7

使用std::numeric_limits和 模运算符。获取目标类型的最大允许值,将其强制转换为源类型,加一,取模,然后强制转换为目标类型。

结果值肯定可以在目标类型中表示,即不会有未定义的行为,但我不知道 MSVC 是否仍会抛出警告。我没有它的副本要检查。

不过,这不会保留负数。它可能可以扩展到这样做,但我不确定如何。(这里已经很晚了。)

template< typename to, typename from >
to narrow_cast( from value ) {
    static_assert( std::numeric_limits< to >::max() < std::numeric_limits< from >::max(),
        "narrow_cast used in non-narrowing context" );

    return static_cast< to >( from %
        ( static_cast< from >( std::numeric_limits< to >::max() ) + 1 ) ) );
}
于 2013-07-27T15:35:04.493 回答
6

这是一个使用一点 C++11 的版本。如果您无权访问constexpr,则可以将其删除。如果您无权访问std::make_unsigned,则可以实现自己的。如果您没有std::enable_if,您也许可以使用 Boost(或自己制作)。它适用于有符号和无符号类型,以及正值和负值。更新:更新以使用浮点类型(以及浮点到整数,反之亦然)。

#include <type_traits>

// From integer type to integer type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_integral<from>::value && std::is_integral<to>::value, to>::type
narrow_cast(const from& value)
{
    return static_cast<to>(value & (static_cast<typename std::make_unsigned<from>::type>(-1)));
}

// For these next 3 versions, you'd be best off locally disabling the compiler warning
// There isn't much magic you can do when floating point types get invovled

// From floating point type to floating point type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_floating_point<from>::value && std::is_floating_point<to>::value, to>::type
narrow_cast(const from& value)
{
    // The best you can do here is a direct cast
    return static_cast<to>(value);
}

// From integer type to floating point type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_integral<from>::value && std::is_floating_point<to>::value, to>::type
narrow_cast(const from& value)
{
    // The best you can do here is a direct cast
    return static_cast<to>(value);
}

// From floating point type to integer type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_floating_point<from>::value && std::is_integral<to>::value, to>::type
narrow_cast(const from& value)
{
    // The best you can do here is a direct cast
    return static_cast<to>(value);
}
于 2013-07-27T16:28:36.787 回答
1

Bjarne 的 TCPPPL 提出了某种窄类型转换,它执行运行时检查以查看某些转换是否丢失信息。取自第 11.5 节的文本:

template<class Target, class Source>
Target narrow_cast(Source v)
{
    auto r = static_cast<Target>(v);
    if (static_cast<Source>(r) != v)
        throw runtime_error("narrow_cast<>() failed");
    return r;
}

基本思想是检查反向转换是否返回原始值;那么我们应该感到满意,因为没有丢失信息。此版本适用于整数/整数转换,因为它们都定义明确(缩小到有符号类型必须由实现明确定义,引发异常将不符合要求)。虽然,您可能应该使用其他重载来改进此定义并放置一些 SFINAE,因为当目标类型太小时,即使对于无符号类型,在浮点/浮点和整数/浮点转换的情况下,此代码也可能导致 UB。

于 2017-09-14T22:47:34.480 回答
-2

小菜一碟!

在这个例子中,让我们取一个指针(在我的机器上是 64 位)并将它“转换”成一个字节:

char narrowme (char *p) {

    union {
        char *p;
        char c[8];
    } x;

    x.p = p;

    return x.c[0];  // pick the byte you want!
}

要使用,只需调用它;例如,c = narrowme (&c2);

如果您愿意,可以使用#define。无论如何,总会涉及到代码。

于 2018-05-29T17:55:04.740 回答