如何用std::index_sequence展开循环实现整数转字节序函数?
无符号整型转二进制表示:用
std::index_sequence替代for循环的实现 问题概述
已实现四个模板函数,分别完成无符号整型到小端、大端、CPU原生端序、翻转CPU端序的二进制字节序列转换,验证功能正确且小端实现速度与std::memcpy相当。现希望用std::index_sequence替换函数中的for循环,实现编译期展开循环以提升效率,但尝试的代码编译失败,需修正实现方式。
原实现代码
#include <vector> using std::vector; typedef vector<uint8_t> bytes; template<class T> inline bytes LittleEndian(const T& data) { size_t size = sizeof(T); bytes _bytes(size); uint8_t mask = 255; for (size_t i = 0, shift = 0; i < size; i++, shift += 8) { _bytes[i] = (data >> shift) & mask; } return _bytes; } template<class T> inline bytes BigEndian(const T& data) { size_t size = sizeof(T); bytes _bytes(size); uint8_t mask = 255; for (size_t i = size, shift = 0; i-- > 0; shift += 8) { _bytes[i] = (data >> shift) & mask; } return _bytes; } template<class T> inline bytes CPU_Endian(const T& data) { size_t size = sizeof(T); bytes _bytes(size); uint8_t* dst = (uint8_t *)_bytes.data(), * src = (uint8_t *) & data; for (size_t i = 0; i < size; i++) { *dst++ = *src++; } return _bytes; } template<class T> inline bytes Flip_CPU_Endian(const T& data) { size_t size = sizeof(T); bytes _bytes(size); uint8_t* dst = (uint8_t *)_bytes.data(), * src = (uint8_t *)&data + size - 1; for (size_t i = 0; i < size; i++) { *dst++ = *src--; } return _bytes; }
尝试的错误实现及编译错误
尝试替换CPU_Endian的代码如下:
template<class T> inline bytes CPU_Endian2(const T& data) { size_t size = sizeof(T); bytes _bytes(size); uint8_t* dst = (uint8_t*)_bytes.data(), * src = (uint8_t*)&data; [&]<std::size_t...N>(std::index_sequence<N...>){ ((*dst++ = *src++),...); }(std::make_index_sequence<size>{}); return _bytes; }
编译错误日志:
Build started at 18:54... 1>------ Build started: Project: hexlify_test, Configuration: Release x64 ------ 1>hexlify_test.cpp 1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(98,3): error C7515: a fold expression must contain an unexpanded parameter pack 1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,3): error C3878: syntax error: unexpected token '(' following 'expression' 1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,3): message : error recovery skipped: '( identifier :: . . . {' 1>C:\Users\Estranger\source\repos\hexlify_test\hexlify_test.cpp(99,35): error C2760: syntax error: '}' was unexpected here; expected ';' 1>Done building project "hexlify_test.vcxproj" -- FAILED. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== ========== Build completed at 18:54 and took 01.796 seconds ==========
错误原因分析
size需为编译期常量:std::make_index_sequence的模板参数必须是编译期已知值,因此size必须声明为constexpr;- 折叠表达式需依赖参数包:原代码中的折叠表达式
((*dst++ = *src++),...)未使用参数包N,编译器无法识别展开逻辑,需将参数包元素加入表达式中(即使仅作为无意义占位)。
正确实现方案
以下实现基于C20模板lambda(若编译器不支持C20,可改用辅助模板函数,见后文补充):
1. CPU端序转换(CPU_Endian)
template<class T> inline bytes CPU_Endian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); const uint8_t* src = reinterpret_cast<const uint8_t*>(&data); uint8_t* dst = _bytes.data(); [&]<std::size_t... N>(std::index_sequence<N...>) { // 用(void)N占位,确保参数包被展开 ((void)N, *dst++ = src[N], ...); }(std::make_index_sequence<size>{}); return _bytes; }
2. 小端转换(LittleEndian)
利用索引N计算位移量,直接赋值对应字节:
template<class T> inline bytes LittleEndian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); constexpr uint8_t mask = 0xFF; [&]<std::size_t... N>(std::index_sequence<N...>) { (_bytes[N] = (data >> (N * 8)) & mask, ...); }(std::make_index_sequence<size>{}); return _bytes; }
3. 大端转换(BigEndian)
通过size-1-N生成递减索引,对应大端字节顺序:
template<class T> inline bytes BigEndian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); constexpr uint8_t mask = 0xFF; [&]<std::size_t... N>(std::index_sequence<N...>) { (_bytes[size - 1 - N] = (data >> (N * 8)) & mask, ...); }(std::make_index_sequence<size>{}); return _bytes; }
也可使用std::make_reverse_index_sequence直接生成递减索引:
template<class T> inline bytes BigEndian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); constexpr uint8_t mask = 0xFF; [&]<std::size_t... N>(std::index_sequence<N...>) { (_bytes[N] = (data >> ((size - 1 - N) * 8)) & mask, ...); }(std::make_reverse_index_sequence<size>{}); return _bytes; }
4. 翻转CPU端序转换(Flip_CPU_Endian)
通过索引映射实现源字节的逆序拷贝:
template<class T> inline bytes Flip_CPU_Endian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); const uint8_t* src = reinterpret_cast<const uint8_t*>(&data); [&]<std::size_t... N>(std::index_sequence<N...>) { _bytes[N] = src[size - 1 - N]; }(std::make_index_sequence<size>{}); return _bytes; }
非C++20兼容版本(辅助模板函数)
若编译器不支持C++20模板lambda,可将展开逻辑封装到辅助模板函数中:
namespace detail { template<class T, std::size_t... N> void copy_little_endian(const T& data, bytes& out, std::index_sequence<N...>) { constexpr uint8_t mask = 0xFF; (out[N] = (data >> (N * 8)) & mask, ...); } template<class T, std::size_t... N> void copy_big_endian(const T& data, bytes& out, std::index_sequence<N...>) { constexpr uint8_t mask = 0xFF; constexpr size_t size = sizeof(T); (out[size - 1 - N] = (data >> (N * 8)) & mask, ...); } template<class T, std::size_t... N> void copy_cpu_endian(const T& data, bytes& out, std::index_sequence<N...>) { const uint8_t* src = reinterpret_cast<const uint8_t*>(&data); (out[N] = src[N], ...); } template<class T, std::size_t... N> void copy_flip_endian(const T& data, bytes& out, std::index_sequence<N...>) { const uint8_t* src = reinterpret_cast<const uint8_t*>(&data); constexpr size_t size = sizeof(T); (out[N] = src[size - 1 - N], ...); } } template<class T> inline bytes LittleEndian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); detail::copy_little_endian(data, _bytes, std::make_index_sequence<size>{}); return _bytes; } template<class T> inline bytes BigEndian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); detail::copy_big_endian(data, _bytes, std::make_index_sequence<size>{}); return _bytes; } template<class T> inline bytes CPU_Endian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); detail::copy_cpu_endian(data, _bytes, std::make_index_sequence<size>{}); return _bytes; } template<class T> inline bytes Flip_CPU_Endian(const T& data) { constexpr size_t size = sizeof(T); bytes _bytes(size); detail::copy_flip_endian(data, _bytes, std::make_index_sequence<size>{}); return _bytes; }
内容的提问来源于stack exchange,提问作者Ξένη Γήινος
相关产品推荐
相关产品推荐

