明确的缩小演员

时间:2013-07-25 14:19:35

标签: c++ casting

我正试图想出一个优雅地忽略丢失数据的缩小演员(一般解决方案)。在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 个答案:

答案 0 :(得分: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 ) ) );
}

答案 1 :(得分: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);
}

答案 2 :(得分: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。

答案 3 :(得分:0)

一块蛋糕!

在这个例子中,让我们取一个指针(我机器中的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(&amp; c2);

如果您愿意,可以使用#define。无论如何,总会有代码参与。