如何创建含默认与可变类型参数的模板?求解决办法
解决FnOnArgs模板参数冲突的可行方案
方案1:用元组包装可变参数,将默认参数移到末尾
把可变参数包Args...打包进std::tuple,让默认模板参数能合法放在列表末尾,核心是通过元组展开参数来验证函数调用可行性:
#include <type_traits> #include <tuple> // 主模板,默认标记为不可调用 template<typename Fn, typename ArgsTuple, typename = void> struct FnOnArgs : std::false_type {}; // 特化版本:如果函数能接受元组展开的参数,则标记为可调用 template<typename Fn, typename... Args> struct FnOnArgs<Fn, std::tuple<Args...>, std::void_t<decltype(std::declval<Fn>()(std::declval<Args>()...))>> : std::true_type {}; // 辅助变量模板,不用手动构造tuple,直接传参数列表即可 template<typename Fn, typename... Args> constexpr bool FnOnArgsV = FnOnArgs<Fn, std::tuple<Args...>>::value;
使用示例:
void foo(int, double) {} // 验证正确参数组合 static_assert(FnOnArgsV<decltype(foo), int, double> == true); // 验证参数数量不足的情况 static_assert(FnOnArgsV<decltype(foo), int> == false);
方案2:调整模板参数结构,利用部分特化绕开限制
主模板只保留函数类型和默认参数,在特化时引入可变参数包,这样既满足可变参数的需求,也让默认参数处于列表末尾:
#include <type_traits> // 主模板,默认不可调用 template<typename Fn, typename = void> struct FnOnArgs : std::false_type {}; // 特化版本:只要函数能接受传入的所有参数,就标记为可调用 template<typename Fn, typename... Args> struct FnOnArgs<Fn, std::void_t<decltype(std::declval<Fn>()(std::declval<Args>()...))>> : std::true_type {}; // 辅助变量模板,简化调用 template<typename Fn, typename... Args> constexpr bool FnOnArgsV = FnOnArgs<Fn, std::void_t<decltype(std::declval<Fn>()(std::declval<Args>()...))>>::value;
使用示例:
void bar(std::string, bool) {} static_assert(FnOnArgsV<decltype(bar), std::string, bool> == true); static_assert(FnOnArgsV<decltype(bar), int, bool> == false);
方案3:C++20 概念(Concept)实现(最简洁)
如果你的项目支持C++20及以上版本,用概念可以直接、直观地实现这个判断逻辑,不需要额外的结构体:
#include <concepts> template<typename Fn, typename... Args> concept FnOnArgs = requires(Fn&& fn, Args&&... args) { std::forward<Fn>(fn)(std::forward<Args>(args)...); };
使用示例:
void baz(float, char) {} static_assert(FnOnArgs<decltype(baz), float, char>); static_assert(!FnOnArgs<decltype(baz), float>);
内容的提问来源于stack exchange,提问作者hczstev
相关产品推荐
相关产品推荐

