在不更改位的情况下更改类型
Changing type without changing bits
我想将一个堆栈变量reinterpret cast
转换为一个以字节为单位的相同大小的无符号整数类型。例如,我可能想要取double
值,并将其强制转换为uint64_t
,注意比特不会被修改。我想以一种普通的方式来做这件事。
如果我在处理指针,我会使用reinterpret_cast<uint64_t*>(double_ptr)
。
我已经想出了一个解决方案,它在reinterpret_cast
上使用了一个肮脏的破解,并且是有效的,但它需要大量的元编程才能获得相当简单的结果。
问题:有更好的方法吗?我确信确实存在,而且我正在使这件事变得比需要的更复杂。
我确实考虑过使用类型为T
和大小适当的int_t
的模板化并集,但这似乎更为棘手,而且似乎在处理未定义的行为。
edit我知道标准并没有规定双精度应该是64位,正如评论中所指出的那样。但是通过一种通用的方法,我将能够得到一个与double大小相同的无符号整数类型,无论它有多大
#include <iostream>
template <typename T, std::size_t S>
struct helper {};
template <typename T>
struct helper<T, 1> {
using type = uint8_t;
};
template <typename T>
struct helper<T, 2> {
using type = uint16_t;
};
template <typename T>
struct helper<T, 4> {
using type = uint32_t;
};
template <typename T>
struct helper<T, 8> {
using type = uint64_t;
};
template <typename T>
using int_type = typename helper<T, sizeof(T)>::type;
template <typename T>
int_type<T> caster(T value) {
int_type<T> v;
*reinterpret_cast<T*>(&v) = value;
return v;
}
int main(void) {
{
auto val = caster(0.);
static_assert(std::is_same<uint64_t, decltype(val)>::value, "no good");
std::cout << sizeof(val)*8 << " " << val << std::endl;
}
{
auto val = caster(0.f);
static_assert(std::is_same<uint32_t, decltype(val)>::value, "no good");
std::cout << sizeof(val)*8 << " " << val << std::endl;
}
{
auto val = caster(-0.);
static_assert(std::is_same<uint64_t, decltype(val)>::value, "no good");
std::cout << sizeof(val)*8 << " " << val << std::endl;
}
{
auto val = caster(-0.f);
static_assert(std::is_same<uint32_t, decltype(val)>::value, "no good");
std::cout << sizeof(val)*8 << " " << val << std::endl;
}
return 0;
}
用gcc编译上面的代码给出:
> g++ --version
g++ (GCC) 4.8.2 20131016 (Cray Inc.)
> g++ -std=c++11 test.cpp && ./a.out
64 0
32 0
64 9223372036854775808
32 2147483648
如果您不想因为违反别名限制(C++11 3.10/10)而有未定义的行为,那么您需要以字符形式访问对象表示:
template <typename T>
int_type<T> caster(const T& value) {
int_type<T> v;
static_assert(sizeof(value) == sizeof(v), "");
std::copy_n(reinterpret_cast<const char*>(&value),
sizeof(T),
reinterpret_cast<char*>(&v));
return v;
}
高质量的编译器将优化拷贝。例如,此程序:
int main() {
return caster(3.14f);
}
在英特尔处理器上有效地优化到CCD_ 8。
在std::conditional_t
和std::enable_if_t
之间,我相信您可以将所有的helper
和int_type
定义压缩为一个自给自足的caster
函数:
template <typename T>
auto caster(T value){return reinterpret_cast<std::conditional_t<sizeof(T) == sizeof(uint8_t),
uint8_t,
conditional_t<sizeof(T) == sizeof(uint16_t),
uint16_t,
conditional_t<sizeof(T) == sizeof(uint32_t),
uint32_t,
enable_if_t<sizeof(T) == sizeof(uint64_t),
uint64_t>>>>&>(value);}
我已经验证了这在gcc 4.9.2和Visual Studio 2015上都有效,如果你只支持C++11,尽管你仍然可以将其转化为一个自给自足的caster
函数:
template <typename T>
typename std::conditional<sizeof(T) == sizeof(uint8_t),
uint8_t,
typename conditional<sizeof(T) == sizeof(uint16_t),
uint16_t,
typename conditional<sizeof(T) == sizeof(uint32_t),
uint32_t,
typename enable_if<sizeof(T) == sizeof(uint64_t),
uint64_t>::type>::type>::type>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
这将选择与传递给它的类型具有相同sizeof
的uint*
并使用它。
我这里有一个std::enable_if
的解释,可能对你有帮助。
显然,这只适用于大小为8、16、32或64位的类型,但如果您想扩展它来处理其他内容,只需添加另一个conditional_t
即可!
如果你是只有会通过8、16、32或64位类型,你可以在你的模板中获得更少的保护:
template <typename T>
auto caster(T value){return reinterpret_cast<std::tuple_element_t<size_t(log2(sizeof(T))), std::tuple<uint8_t,
uint16_t,
uint32_t,
uint64_t>>&>(value);}
这适用于C++14,相当于C++11的是:
template <typename T>
typename std::tuple_element<size_t(log2(sizeof(T))), std::tuple<uint8_t,
uint16_t,
uint32_t,
uint64_t>>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
这比conditional_t
/enable_if_t
模板更不宽容,因为我是如何索引std::tupple
的。size_t
是一个整数类型,因此任何大小小于128位的类型都将强制转换为有效的std::tuple
索引。因此,例如,大小为3位的struct
将被转换为uint16_t
,而所需的结果可能是它无法编译。
- 为什么在这种情况下,bool 类型的输出等于 0?
- C++默认情况下,指针类型数组的元素是否保证初始化为 nullptr?
- 如何在没有实例的情况下获取非静态方法的类型?
- 如何在不破坏现有应用程序的情况下更改 API 中 stl 容器的数据类型?
- 是否可以在不使用 decltype 的情况下推断先前定义的 extern 变量的类型
- 在没有标识符的情况下如何读取复杂的C++类型声明?
- MSVC 在不知道类型的情况下评估上下文(和错误)
- 我们可以在不知道其真实类型的情况下将基类指针转换为派生类指针吗?
- 如何在不给它任何类型(如整数)的情况下定义某物?
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 将类型映射到整数值后,如何在给定整数值的情况下恢复类型?
- 如何在不违反类型别名规则的情况下解释消息负载?
- msgpack:在不知道类型的情况下解包自定义类
- 对于多态类型T,如何在没有类型T实例的情况下获得指向T的虚拟表的指针
- 在这种特殊情况下,我是否需要在共享内存中使用原子类型
- 为什么在以下情况下不需要为依赖类型使用typename
- 有没有办法在没有reinterpret_cast的情况下保存多种类型的指针?
- 如何定义可以在没有联合的情况下容纳uintptr_t或uint32_t的类型?
- 推荐的方法在不初始化值的情况下使数组类型为 std::unique_ptr?
- 为什么默认情况下C++类型不是常量?