static_assert使用疑问:为何仍触发非常量条件编译错误?
参考两个技术讨论的核心结论:
- 第一个讨论指出,
error: non-constant condition for static assertion错误的成因是:constexpr函数可能被传入运行时求值的参数,导致其返回值无法作为编译期常量使用。 - 第二个讨论指出,通过模板函数推导数组大小的方法可行,因为编译器会在编译阶段完成模板实参的推导工作。
但以下代码触发了上述static_assert错误:
In file included from main.cpp:1:
classA.h: In instantiation of ‘void doSomething(A(&)[2]) [with T = int; array2As = A [2]]’:
main.cpp:6:18: required from here
classA.h:65:20: error: non-constant condition for static assertion
65 | static_assert(2 == sz, "Compilation error: array must have exactly 2 elements");
|^~~
compilation terminated due to -Wfatal-errors
代码示例
classA.h
#include <type_traits> #include <cstddef> //#include <iterator> #include <iostream> using std::is_same; //using std::size; using std::size_t; using std::cout; using std::endl; template<typename T> class A; template<typename T> using array2As = A<T>[2]; template<typename T> void doSomething (array2As<T>&); template<template<typename>class A, typename T, size_t N> constexpr size_t cal_size(const A<T>(&)[N]){ return N; } template<typename T> class A{ public: A(T); private: friend void doSomething<>(array2As<T>&); A()=delete; A(const A&)=delete; A& operator=(const A&)=delete; T elem; }; template<typename T> A<T>::A(T elem){ static_assert(is_same<T,int>::value || is_same<T,double>::value, "Compilation error: type must be int or double"); this->elem = elem; } template<typename T> void doSomething (array2As<T>& a){ auto sz = cal_size(a); cout << sz << endl; static_assert(2 == sz, "Compilation error: array must have exactly 2 elements"); const auto& obj1 = a[0]; cout << obj1.elem << endl; const auto& obj2 = a[1]; cout << obj2.elem << endl; return; }
main.cpp
#include "classA.h" int main (){ array2As<int> a = {A(5),A(7)}; doSomething<int>(a); array2As<double> b = {A(1.2),A(6.3)}; doSomething<double>(b); return 0; }
疑问
cal_size和doSomething都是函数模板,按第二个讨论的结论,cal_size返回的sz应该是编译期常量,为什么还是触发了static_assert错误?
核心原因
虽然cal_size是constexpr模板函数,但在doSomething中调用时,sz被声明为普通auto变量——编译器不会默认将其视为编译期常量。constexpr函数的调用结果只有在需要常量表达式的上下文(比如模板参数、static_assert条件)中,才会强制编译期求值;而普通auto变量的赋值上下文没有这个要求,编译器可能会选择运行时求值,即使它本可以在编译期完成。
修复方案
将doSomething中的auto sz = cal_size(a);修改为constexpr auto sz = cal_size(a);,显式强制编译器在编译期完成cal_size的调用,此时sz会成为编译期常量,满足static_assert的要求。
另外,由于你已经通过array2As<T>类型别名限定了数组大小为2,完全可以直接使用编译期常量2,无需调用cal_size,代码会更简洁:
template<typename T> void doSomething (array2As<T>& a){ cout << 2 << endl; // 甚至可以直接去掉这个static_assert,因为array2As<T>已经限定了数组大小 static_assert(2 == 2, "Compilation error: array must have exactly 2 elements"); const auto& obj1 = a[0]; cout << obj1.elem << endl; const auto& obj2 = a[1]; cout << obj2.elem << endl; return; }
补充说明
constexpr函数的调用结果是否为编译期常量,完全取决于调用上下文:
- 若调用出现在需要常量表达式的位置,编译器会尝试编译期求值;
- 若只是赋值给普通变量,编译器可能默认采用运行时求值逻辑。
因此必须显式用constexpr修饰变量,确保其值为编译期常量。
内容的提问来源于stack exchange,提问作者Vinod

