noexcept表达式中的短路求值失效问题咨询
问题:noexcept表达式为何未按预期短路求值?
示例代码
// g++ main.cpp -std=c++23 #include <type_traits> using namespace std; template<typename P> concept pointer_like = is_pointer_v<P> || requires (P p) { p.operator ->(); }; template<pointer_like P> constexpr auto func(P p) noexcept(is_pointer_v<P> || noexcept(p.operator ->())) // error: 'int *' doesn't have 'operator ->' { if constexpr (is_pointer_v<P>) return p; else return p.operator ->(); } int main() { int i = 0; auto pi = func(&i); // error originates here }
问题描述
在上述代码的noexcept说明符中,is_pointer_v<P> || noexcept(p.operator->())未按预期执行短路求值:当P为指针类型时,is_pointer_v<P>为true,但编译器仍会检查p.operator->()的合法性,导致编译错误。用户预期此时应跳过对operator->()的检查,询问这是否属于C++标准的疏漏。
解答
这不是C++标准的疏漏,而是noexcept表达式与if constexpr的求值规则存在本质差异:
if constexpr是模板特化的一种形式,编译期会完全丢弃不成立分支的代码,不会检查其合法性;- 而
noexcept表达式是一个常量表达式,模板实例化时,表达式中的所有子表达式都必须是合法的语法——哪怕逻辑上短路,编译器仍会检查每一部分的语法有效性。原代码中,当P为指针时,p.operator->()本身是非法语法,因此直接触发编译错误。
解决方法
将对operator->()的noexcept判断包裹在requires表达式中,利用requires的规则(表达式不合法时仅返回false,而非编译错误),同时结合逻辑短路实现预期行为:
// g++ main.cpp -std=c++23 #include <type_traits> using namespace std; template<typename P> concept pointer_like = is_pointer_v<P> || requires (P p) { p.operator ->(); }; template<pointer_like P> constexpr auto func(P p) noexcept( is_pointer_v<P> || requires(P p) { noexcept(p.operator ->()); } ) { if constexpr (is_pointer_v<P>) return p; else return p.operator ->(); } int main() { int i = 0; auto pi = func(&i); // 正常编译 }
这里requires(P p) { noexcept(p.operator->()); }会在P支持operator->()时,判断该操作是否为noexcept;若P是指针类型,该requires表达式仅返回false,但不会触发编译错误,结合左边的is_pointer_v<P>为true,整个noexcept条件最终为true,符合预期。
内容的提问来源于stack exchange,提问作者DoZerg
相关产品推荐
相关产品推荐

