C++编译时谓词,用于测试是否可以使用 T 类型的参数调用 F 类型的可调用对象
C++ compile-time predicate to test if a callable object of type F can be called with an argument of type T
我想创建一个编译类型的函数,给定任何可调用对象f
(函数、lambda 表达式、函数对象等)和类型 T
,如果可以用 T
类型的参数调用f
则计算为 true,如果不能,则计算为 false。
例:
void f1(int) { ... }
void f2(const std::string&) { ... }
assert( is_callable_with<int>(f1));
assert(!is_callable_with<int>(f2));
我认为巧妙地使用 SFINAE 规则可以实现这一目标。可能以某种方式像这样:
template<typename T, typename F>
constexpr bool is_callable_with(F&&, typename std::result_of<F(T)>::type* = nullptr) {
return true;
}
template<typename T, typename F>
constexpr bool is_callable_with(F&&) {
return false;
}
但这不起作用,因为如果 F
可以用 T
调用,则两个重载都参与重载解析,并且存在歧义。我想重写它,以便在积极的情况下,第一个重载将由重载分辨率而不是第二个重载分辨率来选择。不确定我是否在这里走在正确的轨道上。
Paul 答案的变体,但遵循标准的 SFINAE 测试模式。再次具有任意参数类型的通用特征A...
:
struct can_call_test
{
template<typename F, typename... A>
static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
f(int);
template<typename F, typename... A>
static std::false_type
f(...);
};
template<typename F, typename... A>
using can_call = decltype(can_call_test::f<F, A...>(0));
然后按照您的要求constexpr
函数:
template<typename T, typename F>
constexpr bool is_callable_with(F&&) { return can_call<F, T>{}; }
查看实时示例。
这将适用于具有任意数量参数的函数、lambda 表达式或函数对象,但对于(指向)成员函数,您必须使用 std::result_of<F(A...)>
。
更新
下面,can_call
有很好的"函数签名"语法std::result_of
:
template<typename F, typename... A>
struct can_call : decltype(can_call_test::f<F, A...>(0)) { };
template<typename F, typename... A>
struct can_call <F(A...)> : can_call <F, A...> { };
像这样使用
template<typename... A, typename F>
constexpr can_call<F, A...>
is_callable_with(F&&) { return can_call<F(A...)>{}; }
我还制作了is_callable_with
可变参数(我不明白为什么它应该仅限于一个参数)并返回与can_call
相同的类型而不是bool
(感谢 Yakk)。
再次,这里活生生的例子。
我会先做一个类型特征:
template<class X = void>
struct holder
{
typedef void type;
};
template<class F, class T, class X = void>
struct is_callable_with_trait
: std::false_type
{};
template<class F, class T>
struct is_callable_with_trait<F, T, typename holder<
decltype(std::declval<F>()(std::declval<T>()))
>::type>
: std::true_type
{};
然后,如果需要,可以将其转换为函数:
template<typename T, typename F>
constexpr bool is_callable_with(F&&)
{
return is_callable_with_trait<F&&, T>::value;
}
template<class F, class T, class = void>
struct is_callable_with_impl : std::false_type {};
template<class F, class T>
struct is_callable_with_impl<F,T,
typename std::conditional<
true,
void,
decltype( std::declval<F>() (std::declval<T>()) ) >::type
> : std::true_type {};
template<class T, class F>
constexpr bool is_callable_with(F &&)
{
return is_callable_with_impl< F, T >::value;
}
它与Paul
发布的解决方案基本相同,我只是更喜欢使用conditional<true, void, decltype( ... ) >
而不是holder
类来避免命名空间污染。
相关文章:
- 通过模板函数对未知类型调用方法
- C++ 在不知道子类型的情况下从父类型调用子方法
- 如何在 cpp 模板中根据实际类型调用整型?
- <未解析的重载函数类型>调用二进制谓词
- 如何使用模板根据数据类型调用函数?
- 统一处理具有协变类型的函数指针(如何使用派生类型调用回调?
- 使用模板化类型调用的模板化函子
- NDK带有JNI的ndk,使用返回类型的非原始类型调用方法
- 如何使用不同的数据类型调用相同功能
- 是否可以根据基于的范围类型调用不同的取消引用运算符重载
- 如何在C++中根据模板类型调用函数
- 重载下标运算符以根据分配的类型调用函数
- C++传递函数以按参数按模板类型调用
- C++类继承从基类型调用子方法
- 根据类型调用不同的函数
- 从数据类型调用构造函数(或任何函数/方法)而不经历两个模板化函数的任何类型的方法
- 循环unique_ptrs的向量,并为运行时类型调用正确的重载
- 根据类型调用不同的函数
- 数据类型调用模板方法
- 未为派生类型调用函数模板专用化