传递Lambda作为参数时GCC无法展开宏?疑似GCC Bug
这不是GCC的Bug,是预处理器的固有规则导致的
首先明确说:这绝对不是GCC的Bug,而是C/C预处理器处理宏参数时的标准行为——所有符合C/C标准的预处理器(包括Clang)都会出现同样的问题。
问题根源
预处理器在解析宏调用时,会把逗号(,)当成宏参数的分隔符——不管这个逗号是出现在函数参数、lambda捕获列表还是其他语法结构里。
看你的代码:
ON_SCOPE_EXIT([a]{}):这里没有逗号,预处理器会把整个[a]{}当成宏的唯一参数,所以编译正常。ON_SCOPE_EXIT([a,b]{}):预处理器看到[a后面的逗号,会直接把[a作为第一个参数,b]{})作为第二个参数,但你的宏只定义了接受1个参数,于是就抛出了"passed 2 arguments, but takes just 1"的错误。
解决办法
有两种简单的方式可以规避这个问题:
用额外的括号包裹lambda表达式
把lambda放在一对括号里,预处理器会把括号内的内容当成一个整体参数,不会拆分逗号:#define ON_SCOPE_EXIT(callback) int main() { int a,b; ON_SCOPE_EXIT([a]{}); // 现在编译正常 ON_SCOPE_EXIT(([a,b]{})); return 0; }使用可变参数宏
重新定义宏为可变参数形式,这样预处理器会把所有逗号包含的内容当成一个可变参数整体:#define ON_SCOPE_EXIT(...) int main() { int a,b; ON_SCOPE_EXIT([a]{}); ON_SCOPE_EXIT([a,b]{}); // 编译正常 return 0; }
当然,如果你要实现真正可用的ScopeGuard,通常会结合RAII类来编写宏,比如:
#include <functional> class ScopeGuard { public: explicit ScopeGuard(std::function<void()> func) : func_(std::move(func)) {} ~ScopeGuard() { if (!dismissed_) func_(); } void dismiss() { dismissed_ = true; } private: std::function<void()> func_; bool dismissed_ = false; }; #define CONCAT_IMPL(a, b) a##b #define CONCAT(a, b) CONCAT_IMPL(a, b) #define ON_SCOPE_EXIT(callback) \ ScopeGuard CONCAT(scope_guard_, __LINE__)(callback)
这种实现下,用括号包裹lambda或者使用可变参数宏都能正常工作,同时真正实现了作用域退出时执行回调的功能。
内容的提问来源于stack exchange,提问作者Li Kailin
相关产品推荐
相关产品推荐

