C++非类型可变参数模板结合宏函数失效问题排查与修复
非类型可变参数模板结合宏调用失败的原因与修复方案
问题概述
直接调用带非类型可变参数模板的成员函数可正常运行;但通过宏封装默认参数后,编译器报错no member named 'handle_func' in 'test_nontype_variadic',移除非类型可变参数模板后宏又能正常工作。
原因分析
- 宏匹配规则限制:C++宏基于文本匹配,你的宏定义
handle_func(...)仅识别handle_func(开头的调用。而test.handle_func<4,3,5>(...)的写法中,<4,3,5>插入到handle_func和(之间,宏无法识别这种带模板参数的结构,因此不会展开。编译器会直接去类中查找handle_func成员,但类中只有_CALLER_EXECUTE_FUNC(即ff_handle_execute_func),因此报出成员不存在的错误。 - 模板参数上下文破坏:即便宏能展开,原调用的
<4,3,5>是给成员函数的非类型模板参数,但宏展开后会将std::source_location::current()插入到函数参数最前面,完全破坏模板参数的上下文,甚至会出现将运行时表达式作为非类型模板参数的非法情况。
修复方法
方法一:用C++默认参数替代宏(推荐)
直接在模板函数中给sl设置默认参数,调整参数顺序让可变参数在前、默认参数在后,完全避免宏的使用:
#include <source_location> #include <iostream> #include <utility> class test_nontype_variadic{ public: template<std::size_t... Sizes, typename UnaryFunction, typename... Args> auto handle_func(UnaryFunction&& unary_op, Args&&... args, const std::source_location& sl = std::source_location::current()){ // 可在此处使用Sizes...和sl return std::forward<UnaryFunction>(unary_op)(std::forward<Args>(args)...); } }; // 使用方式符合预期 int main(){ test_nontype_variadic test; int a = test.handle_func<4, 3, 5>([](auto a, auto b){return a + b;}, 10, 12); std::cout << a << std::endl; }
方法二:调整宏定义以支持模板参数
如果必须使用宏,需定义兼容模板参数的宏,显式区分带/不带模板参数的调用:
#include <source_location> #include <iostream> #include <utility> #define _CALLER_EXECUTE_FUNC ff_handle_execute_func // 不带模板参数的调用 #define handle_func(...) _CALLER_EXECUTE_FUNC(std::source_location::current(), __VA_ARGS__) // 带非类型模板参数的调用,模板参数作为第一个宏参数传入 #define handle_func_t(TPL, ...) _CALLER_EXECUTE_FUNC<TPL>(std::source_location::current(), __VA_ARGS__) class test_nontype_variadic{ public: template<std::size_t... Sizes, typename UnaryFunction, typename... Args> auto _CALLER_EXECUTE_FUNC(const std::source_location& sl, UnaryFunction&& unary_op, Args&&... args){ // 可在此处使用Sizes...和sl return std::forward<UnaryFunction>(unary_op)(std::forward<Args>(args)...); } }; // 使用带模板参数的宏调用 int main(){ test_nontype_variadic test; int a = test.handle_func_t(4,3,5, [](auto a, auto b){return a + b;}, 10, 12); std::cout << a << std::endl; }
注意:宏写法易引入语法兼容性问题,优先推荐方法一的纯C++解决方案。
内容的提问来源于stack exchange,提问作者Sam Moldenha
相关产品推荐
相关产品推荐

