使用constexpr if仍触发ISO C++零长度数组编译错误的解决方法
问题说明
启用-pedantic编译选项时,这段代码会触发ISO C++禁止零长度数组的编译错误:
#include <algorithm> constexpr int DATA_SIZE = 5; constexpr int A_ARRAY_ALLOWED_SIZE = 5; constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE); constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE; class A { int a[A_ARRAY_SIZE]; }; class B { int b[B_ARRAY_SIZE]; }; int main() { A a; if constexpr (B_ARRAY_SIZE) { B b; } return 0; }
哪怕B的对象只在constexpr if的非执行分支里创建,类B的定义本身因为包含零长度数组,还是会被编译器报错。之前用了临时 workaround:
constexpr int Get_B_ARRAY_SIZE() { if (B_ARRAY_SIZE) return B_ARRAY_SIZE; return 1; // 临时规避零长度数组问题 }
现在需要正确的重构方式,实现无-pedantic时的等效行为,用模板元编程就能搞定。
重构方案
核心思路是让类B的结构根据B_ARRAY_SIZE的数值动态调整,避免在数组大小为0时出现非法的零长度数组定义。
方案1:模板特化控制类结构
用模板特化分别处理数组大小为0和大于0的情况,完全符合标准:
#include <algorithm> constexpr int DATA_SIZE = 5; constexpr int A_ARRAY_ALLOWED_SIZE = 5; constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE); constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE; class A { int a[A_ARRAY_SIZE]; }; // 默认模板:对应数组大小为0的情况,空类 template <int N> class B { }; // 特化版本:数组大小>0时,包含数组成员 template <int N> requires (N > 0) class B<N> { int b[N]; }; int main() { A a; if constexpr (B_ARRAY_SIZE > 0) { B<B_ARRAY_SIZE> b; } return 0; }
这里用C++20的requires约束来触发特化,当N>0时才会使用带数组的版本,否则用空类。这样B<0>是合法的空类,不会触发零长度数组错误。
方案2:用std::conditional动态选择成员类型
如果不想用模板特化,也可以用std::conditional搭配std::array来定义成员:
#include <algorithm> #include <array> #include <type_traits> constexpr int DATA_SIZE = 5; constexpr int A_ARRAY_ALLOWED_SIZE = 5; constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE); constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE; class A { int a[A_ARRAY_SIZE]; }; class B { // 数组大小>0时用std::array,否则用空类型占位 using BMemberType = std::conditional_t<(B_ARRAY_SIZE > 0), std::array<int, B_ARRAY_SIZE>, std::monostate>; BMemberType b; }; int main() { A a; if constexpr (B_ARRAY_SIZE > 0) { B b; } return 0; }
std::monostate是C17的空类型,用来占位置;如果是C11/14,自己写个空结构体就行。这种方式下B类始终存在,但成员会根据数组大小自动调整,不会出现零长度数组。
方案3:模板延迟实例化
把B做成模板类,只有在实例化时才检查数组大小,而constexpr if的非执行分支不会触发实例化:
#include <algorithm> constexpr int DATA_SIZE = 5; constexpr int A_ARRAY_ALLOWED_SIZE = 5; constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE); constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE; class A { int a[A_ARRAY_SIZE]; }; template <int N> class B { static_assert(N > 0, "B的数组大小必须为正数"); int b[N]; }; int main() { A a; if constexpr (B_ARRAY_SIZE > 0) { B<B_ARRAY_SIZE> b; } return 0; }
当B_ARRAY_SIZE=0时,constexpr if的分支不会执行,B<0>不会被实例化,自然不会触发零长度数组的错误。加个static_assert还能在意外实例化时给出明确提示。
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

