用宏将动态异常规范替换为noexcept:该方案风险几何?
你的思路本质是通过宏替换把C旧版的动态异常规范映射到C17的noexcept,但这个方案存在多个致命或严重的问题,远不止编码风格层面的问题:
误伤正常的throw语句:宏是纯文本替换,会把库头文件里所有的
throw都换掉——包括函数异常规范里的throw()/throw(...),也包括实际抛出异常的语句(比如throw std::runtime_error("err");)。后者会被替换成noexcept(parameter_pack_is_empty<std::runtime_error("err")>()) std::runtime_error("err");,这完全是语法错误,直接导致编译失败。这是最核心的问题,几乎注定这个方案无法正常工作。语义完全不匹配:原动态异常规范
throw(some_exception_type)的语义是“该函数只会抛出指定类型的异常”,而你替换后的noexcept(false)只是表示“函数可能抛出任意异常”。如果库的实现逻辑依赖于这个类型限制(比如上层代码只捕获指定异常),运行时的异常处理逻辑会彻底失效。复杂语境下的语法错误:如果库头文件里的异常规范包含带逗号的类型(比如
throw(std::pair<int, int>)),宏会把逗号当成可变参数的分隔符,导致parameter_pack_is_empty的模板参数被错误拆分,直接引发语法错误。另外模板函数的异常规范(比如template <typename T> void f() throw(T);)虽然替换后语法合法,但语义完全偏离原设计。标准库头文件被污染:如果库的头文件中包含了标准库头文件(比如
<iostream>、<vector>),在#define throw之后包含这些头文件,会把标准库代码里的throw也替换掉,导致标准库头文件编译报错——标准库中大量存在throw抛出语句,替换后全是语法错误。编译器兼容性差:你依赖GCC对空可变宏的支持,但换用Clang或MSVC时,空宏参数的处理可能不符合预期,导致
throw()的替换结果出现语法问题。
更可行的替代方案
其实GCC在C++17模式下对旧版动态异常规范是兼容的:
throw()会被自动等价为noexcept(true);throw(some_type)会被标记为弃用,但仍能编译,只是会发出警告。
你只需要添加编译选项-Wno-deprecated就能抑制弃用警告,不需要修改或包装头文件,就能正常使用这个库。
内容的提问来源于stack exchange,提问作者Noam Elul

