C++20概念:如何检测类型含任意参数的指定名称函数?
当然可以实现这样的Concept!要忽略函数的参数列表,只检测类型是否包含指定名称的成员函数,我们可以分两种场景来处理:
场景1:不考虑函数重载的情况
如果你的类型中init函数不会被重载(只有单一版本),可以用非常简洁的写法:
#include <concepts> template <typename T> concept Initializable = requires { &T::init; // 仅检测是否存在名为init的成员函数,忽略参数和返回值 }; // 测试用例 struct S { void init() {} }; static_assert(Initializable<S>); // 断言通过 struct T { void init(int) {} }; static_assert(Initializable<T>); // 现在会成功通过! struct U {}; static_assert(!Initializable<U>); // 断言通过,U没有init函数
这个Concept的核心是利用&T::init表达式:只要类型T存在一个非重载的init成员函数,这个表达式就是合法的,Concept就会判定为满足。
场景2:需要支持函数重载的情况
如果类型中可能存在多个重载的init函数,上面的写法会因为&T::init存在歧义而失败。这时我们可以换个思路:检测是否存在至少一组参数,使得调用t.init(...)是合法的。
结合SFINAE和Concept可以实现通用检测:
#include <concepts> #include <type_traits> #include <utility> namespace detail { // 辅助模板:尝试用指定参数调用init template <typename T, typename... Args> auto test_init(int) -> decltype(std::declval<T>().init(std::declval<Args>()...), std::true_type{}); template <typename T, typename... Args> std::false_type test_init(...); // 组合多种常见参数场景,覆盖多数业务需求 template <typename T> struct has_init : std::disjunction< decltype(test_init<T>(0)), // 无参调用 decltype(test_init<T, int>(0)), // 单个int参数 decltype(test_init<T, const char*>(0)), // C字符串参数 decltype(test_init<T, int, double>(0)) // 多参数组合 > {}; } template <typename T> concept Initializable = detail::has_init<T>::value; // 测试重载场景 struct V { void init() {} void init(int) {} }; static_assert(Initializable<V>); // 断言通过,存在至少一个init函数 struct W { void init(std::string) {} }; static_assert(Initializable<W>); // 断言通过
这种方式通过枚举常见的参数组合,确保只要类型有任意版本的init函数,Concept就会判定为满足。你可以根据自己的业务场景,在std::disjunction中添加更多参数组合。
可选:限制返回值为void
如果你需要保留原例子中init返回值为void的限制,可以在Concept中补充返回值检测。比如针对无重载场景:
template <typename T> concept Initializable = requires { &T::init; // 检测init的返回值是否为void requires std::is_same_v<decltype(std::declval<T>().init()), void>; };
如果要同时支持重载和返回值限制,需要调整辅助模板的检测逻辑,确保匹配到的init函数返回值为void即可。
内容的提问来源于stack exchange,提问作者Vinci
相关产品推荐
相关产品推荐

