如何检测类是否存在指定名称的成员函数(支持重载及模板函数)
检测类中是否存在指定名称的成员函数(任意参数/重载/模板)
要检测类是否有名为指定名称的成员函数(不管参数列表、是否重载、是否是模板函数),C++20提供了简洁的解决方案:利用可变参数requires表达式,无需指定具体参数,只要存在任意合法的调用方式即可匹配。
解决方案代码
#include <cstdio> // 检测TYPE是否存在任意参数列表的something成员函数 template<typename TYPE> concept has_something = requires(TYPE t, auto... args) { t.something(args...); }; // 测试用例 struct Foo { void something(int); }; struct Bar { template<typename U> void something(U); }; struct Baz { void something(); void something(int); void something(double, char); }; struct Qux { int something; // 成员变量,非函数 }; struct NoSomething {}; int main() { printf("Foo has something: %d\n", has_something<Foo>); // 输出1 printf("Bar has something: %d\n", has_something<Bar>); // 输出1 printf("Baz has something: %d\n", has_something<Baz>); // 输出1 printf("Qux has something: %d\n", has_something<Qux>); // 输出0 printf("NoSomething has something: %d\n", has_something<NoSomething>); // 输出0 return 0; }
原理说明
requires(TYPE t, auto... args)声明了一个可变参数包args,允许编译器尝试任意参数组合。t.something(args...)要求存在至少一组参数,使得调用something是合法的。这自然覆盖了:- 任意参数列表的非重载成员函数
- 任意重载的成员函数
- 任意可推导模板参数的成员函数模板
- 不会误判成员变量:成员变量无法通过
()调用,因此这类情况会被正确排除。
旧标准兼容方案(C++17及更早)
如果需要兼容C++17及更早版本,可使用SFINAE结合可变参数模板实现,但代码复杂度更高:
#include <cstdio> #include <type_traits> namespace detail { // 尝试用任意参数调用something template<typename T, typename... Args> auto test(int) -> decltype(std::declval<T>().something(std::declval<Args>()...), std::true_type{}); template<typename T, typename... Args> std::false_type test(...); // 检测是否存在至少一种合法调用 template<typename T, typename... Args> constexpr bool has_call = decltype(test<T, Args...>(0))::value; } template<typename T> constexpr bool has_something = detail::has_call<T> || detail::has_call<T, int> || detail::has_call<T, int, int> || detail::has_call<T, double, char> || detail::has_call<T, const char*>; // 测试用例同前 int main() { printf("Foo has something: %d\n", has_something<Foo>); // 1 printf("Bar has something: %d\n", has_something<Bar>); // 1 printf("Baz has something: %d\n", has_something<Baz>); // 1 printf("Qux has something: %d\n", has_something<Qux>); // 0 printf("NoSomething has something: %d\n", has_something<NoSomething>); // 0 return 0; }
这种方法需要枚举常见的参数组合,无法100%覆盖所有可能,但能满足大部分实用场景。
内容的提问来源于stack exchange,提问作者geza
相关产品推荐
相关产品推荐

