C++编写包装libgit2调用的模板函数报错问题咨询
问题描述
我在使用libgit2库时,希望封装库的调用逻辑,当调用返回错误时自动抛出异常,以此实现对所有返回通用错误码类型函数的统一调用。我最初尝试通过模板函数实现该功能,但遇到了问题,初始实现代码如下:
template<typename F, typename... Args> inline void invoke(Args&&... args) { git_error_code errCode = F(std::forward<Args>(args)...); if (errCode != GIT_OK) { throw GitErrorException(errCode); } }
该版本代码触发编译器警告,提示使用了无效类型,因此我尝试改用类型别名的实现方式,代码如下:
template<typename ... Args> using F = int (*)(Args&&... args); template<F f, typename... Args> inline void invoke(Args&&... args) { git_error_code errCode = f(std::forward<Args>(args)...); if (errCode != GIT_OK) { throw GitErrorException(errCode); } }
但该版本在按如下方式调用时依然无法正常工作:
Git::invoke<git_branch_name>(&name, _ref);
请问在该场景下我是否必须依赖宏才能实现所需的封装逻辑?
解答
你完全不需要依赖宏就能实现这个封装,之前两个版本的写法都存在语法层面的问题:
- 第一个版本的错误在于,你把函数类型
F当成了可调用对象直接在函数体内执行,但模板参数推导阶段根本不会把传入的函数指针识别为F对应的可调用实体,你只是声明了类型形参F,却没有传入对应的实参,自然会触发无效类型的警告。 - 第二个版本的类型别名写法本身有逻辑缺陷:
template<typename ... Args> using F = int (*)(Args&&... args);这种写法在作为非类型模板参数声明时,F本身是依赖模板实参的别名,C++不允许这种未完全实例化的模板别名直接作为非类型模板参数的类型,编译器根本无法确定f的具体类型,自然无法匹配调用。
兼容C++11及以上标准的实现
直接把目标函数作为普通参数传入,让编译器自动完成类型推导即可,不需要把函数放到模板参数列表里:
template<typename F, typename... Args> inline void invoke(F&& func, Args&&... args) { git_error_code errCode = std::forward<F>(func)(std::forward<Args>(args)...); if (errCode != GIT_OK) { throw GitErrorException(errCode); } }
调用时把目标函数作为第一个参数传入即可:
Git::invoke(git_branch_name, &name, _ref);
适配原有调用形式的C++17实现
如果你坚持要把目标函数作为编译期模板参数传入,匹配你最初写的调用形式,在C++17及以上版本可以用自动类型的非类型模板参数实现:
template<auto func, typename... Args> inline void invoke(Args&&... args) { git_error_code errCode = func(std::forward<Args>(args)...); if (errCode != GIT_OK) { throw GitErrorException(errCode); } }
这种写法可以直接兼容你之前的调用方式:Git::invoke<git_branch_name>(&name, _ref);,不需要额外把函数指针作为运行时实参传入。
两种实现都完全不需要依赖宏,第一种写法兼容性更强,适配绝大多数现有C编译环境,第二种写法更贴近你最初预期的调用形式,可根据自己使用的C标准版本选择。
内容的提问来源于stack exchange,提问作者Enigmatic
相关产品推荐
相关产品推荐

