EDG与GCC/Clang对constexpr右值引用常量表达式的处理差异及标准判定
C++中可修改constexpr静态右值引用在static_assert中的合法性分析
先看问题中的代码:
template<int N> struct s{ constexpr static const int&& n = N; constexpr static int&& p = N; }; template<typename T> void f() { static_assert(T::n == 1); } template<typename T> void g() { static_assert(T::p == 1); } int main(){ f<s<1>>(); // 所有4款编译器均支持 g<s<1>>(); // 仅EDG和MSVC支持,Clang和GCC拒绝 }
标准层面的行为判定
根据最新C标准(以C20为例),g<s<1>>()中的static_assert(T::p == 1)属于无效代码,Clang和GCC的拒绝行为符合标准,MSVC和EDG的接受行为不符合标准,理由如下:
constexpr引用的初始化规则
constexpr static int&& p = N;的初始化本身是合法的:模板参数N是常量表达式,符合constexpr引用绑定到「用常量表达式初始化、且生存期被延长至引用生存期的临时对象」的规则。但问题核心在于被引用对象的属性。常量表达式的求值限制
static_assert要求条件表达式必须是常量表达式,而常量表达式的求值有严格约束:
根据C++标准[expr.const]条款,对对象进行左值到右值的转换时,仅当对象满足以下条件之一才被允许:- 是用常量表达式初始化的
const对象,且类型为算术/枚举/指针/成员指针类型; - 本身是
constexpr对象; - 是字符串字面量;
- 是用常量表达式初始化的临时对象,且其生存期在常量表达式求值期间未结束。
- 是用常量表达式初始化的
T::p的违规点T::p引用的是一个可修改的int临时对象(s<1>::p = 3;可编译运行就能证明这一点):- 该对象不是
const类型,不满足第一个条件; - 它本身不是
constexpr对象(仅被constexpr引用绑定),不满足第二个条件; - 它既不是字符串字面量,也不是仅在编译期存在的临时对象(其生存期被延长至程序运行结束,编译期求值时该对象尚未存在),因此无法通过常量表达式的合法性校验。
- 该对象不是
综上,static_assert(T::p == 1)中的表达式不属于合法的常量表达式,代码应被编译器拒绝。
内容的提问来源于stack exchange,提问作者Anoop Rana
相关产品推荐
相关产品推荐

