我正试图想出一个优雅地忽略丢失数据的缩小演员(一般解决方案)。在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
)。
答案 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。无论如何,总会有代码参与。