明确的缩小演员

Kor*_*icz 5 c++ casting

我正试图想出一个优雅地忽略丢失数据的缩小演员(一般解决方案).在Visual Studio中,丢失数据的缩小版本会触发"运行时检查失败#1".我不想把它关闭,而是我试图实现一个narrow_cast优雅地缩小演员阵容并且不会触发运行时检查.

Visual Studio建议:

char c = (i & 0xFF);
Run Code Online (Sandbox Code Playgroud)

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

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 );
}
Run Code Online (Sandbox Code Playgroud)

虽然它有效(VS似乎完全没有丢失常量数据),但它既不完整(不支持非整数数据),也不正确(我认为它对签名值不能正常工作).

有关更好的解决方案或更好的narrow_mask实现的任何建议吗?

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

Pot*_*ter 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 ) ) );
}
Run Code Online (Sandbox Code Playgroud)


Cor*_*lks 5

这是一个使用一点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);
}
Run Code Online (Sandbox Code Playgroud)