edA*_*a-y 7 c++ language-lawyer c++11
我担心我可能会遗漏一些微不足道的东西,但是如果你想保留原始的无符号值,似乎没有实际的安全方式来转换为签名类型.
在reinterpret_cast上,5.2.10没有列出整数到整数的转换,因此没有定义(而static_cast定义没有额外的转换).关于积分转换4.7.3基本上说大型无符号的转换将是实现定义的(因此不可移植).
这似乎是有限的,因为我们知道,例如,uint64_t在任何硬件上,应该可以安全地转换为a int64_t和back而不会改变值.另外,标准布局类型的规则实际上保证了安全转换,如果我们memcpy在两种类型之间而不是分配.
我对么?有没有合理的理由为什么不能reinterpret_cast在整数类型之间有足够的大小?
澄清:肯定无符号的签名版本不保证值,但它只是我考虑的往返(unsigned => signed => unsigned)
更新:仔细查看答案并交叉检查标准,我相信memcpy实际上并没有保证工作,因为它没有说明这两种类型是布局兼容的,也不是char类型.进一步更新,深入研究C-standard这个memcpy应该可以工作,因为目标的大小足够大并且它复制了字节.
答案:似乎没有技术上的理由说明为什么不允许reinterpret_cast执行此转换.对于这些固定大小的整数类型,a memcpy保证可以工作,实际上只要中间可以表示所有的位模式,任何中间类型都可以使用(浮点数可能很危险,因为可能存在陷阱模式).通常,您不能在任何标准布局类型之间进行memcpy,它们必须是兼容的或char类型.这里的注明是特殊的,因为它们有额外的保证.
正如您所指出的,memcpy 是安全的:
uint64_t a = 1ull<<63;
int64_t b;
memcpy(&b,&a,sizeof a);
Run Code Online (Sandbox Code Playgroud)
值 b 仍然是实现定义的,因为 C++ 不需要二进制补码表示,但将其转换回来将为您提供原始值。
正如 Bo Persson 指出的那样,int64_t 将是二进制补码。因此,memcpy 应该产生一个有符号值,该值的简单整数转换回无符号类型被明确定义为原始无符号值。
uint64_t c = b;
assert( a == c );
Run Code Online (Sandbox Code Playgroud)
另外,您可以实现自己的“signed_cast”以使转换变得容易(我不利用二进制补码,因为它们不限于 intN_t 类型):
template<typename T>
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value,T>::type
signed_cast(typename std::make_unsigned<T>::type v) {
T s;
std::memcpy(&s,&v,sizeof v);
return s;
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value,T>::type
signed_cast(typename std::make_signed<T>::type v) {
T s;
std::memcpy(&s,&v,sizeof v);
return s;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
935 次 |
| 最近记录: |