定义明确的窄型铸造
Well defined narrowing cast
我正试图提出一种缩小强制转换(一种通用解决方案),它可以优雅地忽略丢失的数据。在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似乎可以很好地处理常数上的数据丢失),但它既不完整(不支持非整数数据),也不正确(我认为它对有符号值不正确)。
对于更好的解决方案或更好的窄掩码实现,有什么建议吗?
编辑:面对这个问题是VS特定的评论,我检查了标准文档,缩小static_cast
的结果似乎取决于实现。因此,这个问题可以更好地表述为创建一个定义良好的(ergo,而不是依赖于实现的)窄型转换。我不太关心结果值的细节,只要它定义良好并取决于类型(而不是return 0
)。
这里有一个版本使用了一点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);
}
使用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 ) ) );
}
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。
小菜一碟!
在这个例子中,让我们取一个指针(在我的机器中为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=窄距(&c2);
如果您愿意,可以使用#define。无论如何,总会涉及到代码。
- 是否可以使用默认泛型参数在C++中定义 lambda?
- C++:如何从另一个谓词定义泛型而不是谓词
- 泛型成员函数定义
- 如何确保 "<<" 运算符适用于模板化 ADT 定义中的任何泛型类型?
- 如何在 rxcpp 自定义运算符中正确推断泛型
- C .当T类需要A类模板参数时,如何定义A类T型的模板参数
- 对c++中的泛型对象的未定义引用
- 定义用于迭代不同容器类型的泛型转换迭代器
- 使用用户定义函数展开泛型循环
- 在 c++ 对象上使用 extern 时未定义引用,但不是整型
- 用自定义deleter封装C型指针的常见方法
- 如何在参数为泛型类型的模板类中为方法类型化定义函数签名
- 访问静态 constexpr 浮点型成员时的未定义引用
- C++:如何使用泛型返回类型定义虚函数
- 未定义的符号..运算符 new(无符号长整型)
- 定义明确的窄型铸造
- 具有泛型类方法定义的长模板参数列表
- c++如何在泛型编程中声明自定义数组
- 是对定义良好的有符号整型进行位操作的结果
- 在Swift中定义显式泛型方法