简体   繁体   English

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM