如何创建含未初始化数组的std::tuple以避免零初始化开销?
解决方案:创建未初始化的std::tuple并保留NRVO
你希望通过std::tuple从函数返回一组值,避免编译器对tuple内的2KB数组进行清零初始化,同时保留具名返回值优化(NRVO),不想用自定义类实现。原代码中编译器会自动填充tuple内存为0,以下是简便优化方案:
原代码
#include <tuple> #include <cstdint> #include <cstddef> const size_t SHA256_DIGEST_LENGTH = 256 * 8; unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); struct uuid { uint32_t time_low; uint16_t time_mid; uint16_t time_hi; uint8_t bits[8]; }; void make_uuid(uuid *result); using uuid_pair = std::tuple<uuid, uint8_t[SHA256_DIGEST_LENGTH]>; uuid_pair make_uuid() { uuid_pair result; make_uuid(&std::get<uuid>(result)); SHA256((uint8_t *)&std::get<uuid>(result), sizeof(uuid), &std::get<1>(result)[0]); return result; }
核心优化方案:用std::array替代C-style数组
std::tuple对C-style数组的默认构造可能触发值初始化(即清零),而std::array作为聚合类型,默认初始化不会自动清零元素。替换后完全保留NRVO,代码改动极小:
修改后的代码
#include <tuple> #include <cstdint> #include <cstddef> #include <array> // 引入std::array头文件 const size_t SHA256_DIGEST_LENGTH = 256 * 8; unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); struct uuid { uint32_t time_low; uint16_t time_mid; uint16_t time_hi; uint8_t bits[8]; }; void make_uuid(uuid *result); // 用std::array替代C-style数组 using uuid_pair = std::tuple<uuid, std::array<uint8_t, SHA256_DIGEST_LENGTH>>; uuid_pair make_uuid() { uuid_pair result; // 默认初始化,元素均未被清零 make_uuid(&std::get<0>(result)); // 用data()获取数组首地址,更规范 SHA256(reinterpret_cast<uint8_t*>(&std::get<0>(result)), sizeof(uuid), std::get<1>(result).data()); return result; // NRVO依然生效,无额外拷贝 }
原理说明
- 避免清零的原因:
std::array<T, N>的默认初始化(std::array<T, N> arr;)不会对元素进行值初始化(即清零),仅分配内存;而原代码中的C-style数组作为std::tuple元素时,编译器可能因语言规则或编译选项(如-ftrivial-auto-var-init=zero)触发值初始化,导致内存被清零。 - 保留NRVO:函数返回的是具名局部变量
result,编译器会直接在调用方的内存空间构造该tuple,完全避免拷贝或移动操作,性能不受影响。
额外提示(C++20专属)
如果必须使用C-style数组且基于C++20标准,可利用std::tuple的聚合类型特性,直接构造未初始化的tuple:
uuid_pair result; // C++20中std::tuple是聚合类型,默认初始化不会触发值初始化
但此方法依赖C++20标准,且部分编译器可能因默认选项仍触发清零,因此std::array的方案兼容性和可靠性更高。
内容的提问来源于stack exchange,提问作者Kirill Frolov
相关产品推荐
相关产品推荐

