[英]C++ compile-time predicate to test if a callable object of type F can be called with an argument of type T
I would like to create a compile-type function that, given any callable object f
(function, lambda expression, function object, ...) and a type T
, evaluates to true, if f
can be called with an argument of type T
, and false if it cannot. 我想创建一个编译类型函数,给定任何可调用对象
f
(函数,lambda表达式,函数对象,...)和类型T
,如果可以使用类型为T
的参数调用f
,则求值为true如果不能,则为假。
Example: 例:
void f1(int) { ... }
void f2(const std::string&) { ... }
assert( is_callable_with<int>(f1));
assert(!is_callable_with<int>(f2));
I'm thinking that a clever use of the SFINAE rule could achieve this. 我认为巧妙地使用SFINAE规则可以实现这一目标。 Possibly somehow like this:
可能有点像这样:
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;
}
But this doesn't work, because if F
is callable with T
, both overloads participate in the overload resolution and there is an ambiguity. 但这不起作用,因为如果
F
可以用T
调用,则两个重载都参与重载决策并且存在歧义。 I'd like to rewrite it so in the positive case, the first overload would be picked by the overload resolution over the second one. 我想重写它,所以在正面情况下,第一个重载将由第二个重载决策选择。 Not sure if I'm even on the right track here though.
不确定我是否在这里的正确轨道。
A variant of Paul's answer , but following the standard SFINAE test pattern. 保罗答案的一个变体,但遵循标准的SFINAE测试模式。 Again a generic trait with arbitrary parameter types
A...
: 同样是具有任意参数类型
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));
Then a constexpr
function as you requested: 然后根据您的要求提供
constexpr
功能:
template<typename T, typename F>
constexpr bool is_callable_with(F&&) { return can_call<F, T>{}; }
Check live example . 查看实时示例 。
This will work with functions, lambda expressions, or function objects with arbitrary number of arguments, but for (pointers to) member functions you'll have to use std::result_of<F(A...)>
. 这将适用于具有任意数量参数的函数,lambda表达式或函数对象,但对于(指向)成员函数,您必须使用
std::result_of<F(A...)>
。
UPDATE UPDATE
Below, can_call
has the nice "function signature" syntax of std::result_of
: 下面,
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...> { };
to be used like this 像这样使用
template<typename... A, typename F>
constexpr can_call<F, A...>
is_callable_with(F&&) { return can_call<F(A...)>{}; }
where I've also made is_callable_with
variadic (I can't see why it should be limited to one argument) and returning the same type as can_call
instead of bool
(thanks Yakk). 我已经使
is_callable_with
变量(我不明白为什么它应该限于一个参数)并返回与can_call
相同的类型而不是bool
(感谢Yakk)。
Again, live example here . 再次, 这里的实例 。
I would make a type trait first: 我会先做一个类型特征:
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
{};
And then if you want, you can turn it into a function: 然后,如果你想,你可以把它变成一个函数:
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;
}
It is basically the same solution as the one posted by Paul
, I just prefer to use conditional<true, void, decltype( ... ) >
instead of an holder
class to avoid namespace pollution. 它与
Paul
发布的解决方案基本相同,我更喜欢使用conditional<true, void, decltype( ... ) >
而不是holder
类来避免命名空间污染。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.