C++20中常量表达式绑定引用到临时对象的合法场景与示例验证
根据C++20标准,以下代码示例是否合法?
inline constexpr auto const& v = bool{}; // #1 - 所有编译器均通过 static_assert([]{ constexpr auto const& x = bool{}; // #2 - clang不通过,gcc不通过,msvc通过 return not x; }()); struct s { constexpr s(auto const&) {} }; static_assert([]<s = bool{}>{ return true; }()); // #3 - clang不通过,gcc通过,msvc通过
各编译器报错信息
针对示例#2的报错
Clang错误信息:
<source>:5:27: 错误:constexpr变量'x'必须由常量表达式初始化 5 | constexpr auto const& x = bool{}; | ^ ~~~~~~ <source>:5:27: 注释:引用临时对象不属于常量表达式 <source>:5:31: 注释:临时对象在此处创建 5 | constexpr auto const& x = bool{}; |
GCC错误信息:
<source>:5:36: 错误:'<anonymous>'不是常量表达式 5 | constexpr auto const& x = bool{}; | ^ <source>: 全局作用域: <source>:7:2: 错误:static_assert的条件不是常量 4 | static_assert([]{ | ~~~ 5 | constexpr auto const& x = bool{}; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | return not x; | ~~~~~~~~~~~~~ 7 | }()); | ~^~ <source>:7:2: 错误:在常量表达式中调用了'<lambda()>' <source>:4:15: 注释:'<lambda()>'无法作为'constexpr'函数使用,原因是: 4 | static_assert([]{ | ^
针对示例#3的报错
Clang错误信息:
<source>:10:15: 错误:找不到匹配的函数调用对象,类型为'(lambda at <source>:10:15)' 10 | static_assert([]<s = bool{}>{ return true; }()); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <source>:10:15: 注释:候选模板被忽略:替换失败:在转换常量表达式中,从'bool'转换为's'会将引用绑定到临时对象 10 | static_assert([]<s = bool{}>{ return true; }()); | ^ ~~~~
合法性分析
示例#1:合法
所有编译器均通过,符合C++20标准。全局/命名空间作用域的constexpr引用绑定到临时对象时,临时对象的生命周期会被延长至与引用相同,这种初始化属于常量表达式的允许范畴。
示例#2:不合法
根据C++20标准,块作用域的constexpr引用直接绑定到临时对象不属于合法的常量表达式。这类constexpr变量要求初始化表达式必须是常量表达式,但临时对象在初始化完成后会立即销毁,无法满足“引用目标具有足够生命周期”的要求。因此Clang和GCC的报错符合标准,MSVC的实现存在偏差。
示例#3:合法
根据C++20标准,模板参数的默认实参允许通过转换常量表达式将临时对象绑定到引用(此处s的构造函数接受const auto&,bool{}临时对象在转换为s时绑定到构造函数的引用参数,整个转换过程满足常量表达式要求)。Clang的报错属于实现缺陷,GCC和MSVC的行为符合标准。
核心问题:常量表达式中何时允许将引用绑定到临时对象?
根据C++20标准,合法场景包括:
- 全局/命名空间作用域的
constexpr引用初始化:临时对象生命周期被延长至与引用一致,初始化过程属于合法常量表达式。 - 常量表达式求值过程内的临时对象绑定:临时对象在常量表达式内部创建,且其生命周期覆盖引用的整个使用周期(比如仅在表达式求值期间使用该引用)。
- 模板参数的转换常量表达式:模板参数默认值通过转换构造函数(接受引用参数)创建对象时,临时对象可绑定到构造函数的引用,只要整个转换过程满足常量表达式要求。
constexpr函数的形参引用绑定:形参引用绑定到实参临时对象,且临时对象生命周期覆盖函数调用过程。
不允许的场景:
- 块作用域的
constexpr引用直接绑定到临时对象:这类constexpr变量要求初始化表达式是常量表达式,但临时对象初始化后立即销毁,无法在后续常量表达式求值中被引用。
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

