C++非constexpr参数创建constexpr数组及static_assert使用方法
我从相关技术解答了解到,函数形参args不属于常量表达式(constant expression)。现有问题如下:应当如何修改下述示例程序,才能在第一个CreateArrConst函数变体中使用static_assert时不触发编译错误?
示例代码如下:
#include <array> template<typename... Args> auto constexpr CreateArrConst(Args&&... args) { std::array arr { args... }; //i want to have an static_assert here without error return arr; } template<typename... Args> auto constexpr CreateArrConst_NotWorking(Args&&... args) { constexpr std::array arr { args... }; static_assert(arr.back() == 4); return arr; } int main() { static_assert(CreateArrConst(4).back() == 4); // uncomment this to reproduce compile error // static_assert(CreateArrConst_NotWorking(4).back() == 4); return 0; }
问题原因与修改方案
编译报错的核心规则和你查到的结论完全一致:constexpr函数的形参永远不属于常量表达式。
哪怕你在编译期语境(比如static_assert里)调用这个constexpr函数,函数体内的形参args仍然不被视为编译期常量:你既不能用它初始化constexpr修饰的数组,也不能对依赖它值的表达式直接做static_assert检查——本质是因为constexpr函数同时支持运行期调用,如果允许在函数体内写依赖形参值的静态断言,当函数接收运行期传入的参数时,断言根本无法在编译阶段执行,C++标准从规则层面就禁止这种写法。
如果要把static_assert写在函数内部且不触发编译错误,必须让断言依赖的值是编译期确定、和函数运行期调用无关的常量,可选实现方案如下:
- 方案1(C++20及以上可用,最简洁):把传入的数值从函数形参改为非类型模板参数,用
consteval修饰函数保证其仅能在编译期执行,修改后代码如下:
#include <array> template<auto... args> consteval auto CreateArrConst() { constexpr std::array arr{ args... }; // 此处arr所有元素都是编译期常量,静态断言可正常通过 static_assert(arr.back() == 4); return arr; } int main() { static_assert(CreateArrConst<4>().back() == 4); return 0; }
- 方案2(C++20及以上可用,保留原调用形式):如果希望保留
CreateArrConst(4)的函数式调用写法、不需要显式写尖括号传模板参数,可以借助std::integral_constant抬升参数的常量属性,包装后调用形式和原代码完全一致,同样支持函数内静态断言:
#include <array> #include <utility> template<auto V> using Const = std::integral_constant<decltype(V), V>; template<auto... Args> consteval auto CreateArrConstImpl(std::integral_constant<decltype(Args), Args>...) { constexpr std::array arr{ Args... }; static_assert(arr.back() == 4); return arr; } template<typename... Args> consteval auto CreateArrConst(Args&&...) { return CreateArrConstImpl(Const<Args{}>{}...); } int main() { static_assert(CreateArrConst(4).back() == 4); return 0; }
注意:上述两种方案都要求传入参数必须是编译期常量。如果你需要函数支持运行期传参,就不可能在函数体内写依赖参数值的
static_assert,这种场景下把断言放在编译期调用位置(比如原代码main函数中的写法)即可。
内容的提问来源于stack exchange,提问作者SupAl
相关产品推荐
相关产品推荐

