如何无需中间变量初始化含数组的constexpr结构体?
针对你的需求,以下几种方案可以避免使用中间变量,同时满足constexpr初始化的要求,你可以根据实际场景选择:
方案1:使用std::initializer_list<const int>作为成员类型
这是最简洁的写法,直接支持列表初始化,完全不需要中间变量,且兼容C++11及以上的constexpr规则:
#include <initializer_list> struct MyStruct { std::initializer_list<const int> a; std::initializer_list<const int> b; // ... 其他类数组字段 }; constexpr MyStruct foo{ .a = {1,2,3}, // 直接初始化,无需中间变量 .b = {5,6} }; constexpr MyStruct bar{ .a = {1,2}, .b = {1,2,3,4,5} };
注意:std::initializer_list的底层数组在constexpr初始化语境下会被编译器视为静态存储期的常量,因此结构体成员的引用是安全的;但如果在运行时用非常量表达式初始化,需要注意临时数组的生命周期问题——不过你的场景是constexpr初始化,所以无需担心。
方案2:用constexpr lambda配合std::span
如果必须保留std::span作为成员类型,可以通过constexpr lambda内部定义static constexpr数组来规避中间变量,数组的静态存储期能保证std::span绑定的有效性:
#include <span> struct MyStruct { std::span<const int> a; std::span<const int> b; // ... 其他类数组字段 }; constexpr MyStruct foo{ .a = []() constexpr { static constexpr auto arr = {1,2,3}; return std::span{arr}; }(), .b = []() constexpr { static constexpr auto arr = {5,6}; return std::span{arr}; }() }; constexpr MyStruct bar{ .a = []() constexpr { static constexpr auto arr = {1,2}; return std::span{arr}; }(), .b = []() constexpr { static constexpr auto arr = {1,2,3,4,5}; return std::span{arr}; }() };
lambda内部的static constexpr数组拥有静态存储期,因此std::span的引用在整个程序生命周期内有效,且完全符合constexpr要求。
方案3:模板化结构体,使用std::array作为成员
如果每个结构体实例的数组字段大小是固定的,可以将结构体模板化,用对应大小的std::array作为成员,既类型安全又无需中间变量:
#include <array> template<size_t SizeA, size_t SizeB> struct MyStruct { std::array<int, SizeA> a; std::array<int, SizeB> b; // ... 其他类数组字段可添加更多模板参数 }; // 显式指定数组大小,直接初始化 constexpr MyStruct<3,2> foo{ .a = {1,2,3}, .b = {5,6} }; constexpr MyStruct<2,5> bar{ .a = {1,2}, .b = {1,2,3,4,5} };
缺点:不同大小的数组会生成不同的结构体类型,如果需要统一类型的结构体实例,这个方案不适用;但如果每个实例的数组大小固定,这是类型最安全的选择。
方案4:C++23环境下用std::to_array配合std::span
如果你的项目已经升级到C++23,可以直接用std::to_array生成临时数组并绑定到std::span,编译器会在constexpr语境下将临时数组优化为静态存储的常量:
#include <span> #include <array> // 用于std::to_array struct MyStruct { std::span<const int> a; std::span<const int> b; // ... 其他类数组字段 }; constexpr MyStruct foo{ .a = std::span{std::to_array<const int>({1,2,3})}, .b = std::span{std::to_array<const int>({5,6})} };
注意:需要确保编译器支持C++23(如GCC 12+、Clang 15+、MSVC 2022+)。
内容的提问来源于stack exchange,提问作者MilesF

