找出类型是否可调用

时间:2018-09-26 13:48:24

标签: c++ c++11 templates

我需要弄清楚模板参数是否是具有非无效返回值的可调用对象。

我已经定义了以下内容:

template<class T, class U = T>
struct is_callable
{
    constexpr static const bool val = false;
};

template<class T>
struct is_callable<T, std::result_of_t<T()>>
{
    constexpr static const bool val = true;
};

template<class T>
constexpr bool is_func = is_callable<T>::val;

但是以下所有变量均为false

auto lambda = []() {return 3.0; };

auto intIsFunc = is_func<int>; //false -- ok
auto functionIsFunc = is_func<std::function<int()>>; //false -- wrong
auto lambdaIsFunc = is_func<decltype(lambda)>; //false -- wrong
  • 代码有什么问题?

  • 如何改进is_func不仅在可调用对象上返回true,而且还可以使用可构造的返回类型(在std::is_constructible_v<>处使用)进行调用?

2 个答案:

答案 0 :(得分:3)

使用enable_if

template <typename T, typename = void>
struct is_callable_non_void_return : public std::false_type {};

template <typename T>
struct is_callable_non_void_return<
    T,
    std::enable_if_t<!std::is_same_v<void, std::result_of_t<T()>>>>
    : public std::true_type {};

这是因为SFINAE而起作用:替换失败不是错误。

编译器将第二个is_callable_non_void_return作为第一个的特化,并尝试通过实例化enable_if来匹配模板:首先result_of_t,然后是is_same_v。如果任何一个失败,则会发生替换失败,并且编译器将退回到一般情况。

答案 1 :(得分:1)

您误解了模板专业化的含义。

template<class T, class U = T>
struct is_callable

这是主要专长。

当您执行is_callable<Foo>时,表示您正在输入is_callable<Foo, Foo>。您在其他专业领域所拥有的一切都无法改变。

template<class T>
struct is_callable<T, std::result_of_t<T()>>

这试图与传递的参数进行匹配,从不更改它们。

因此,对于is_callable<Foo>,这是is_callable<Foo,Foo>。将TFoo进行匹配很容易;所以现在有了T=Foo。然后,我们查看依赖于T-std::result_of_t<T()>std::result_of_t<Foo()>的那些。得出的结果是大约用Foo调用()的结果。

如果我们有:

struct Foo {
  Foo operator(){ return {}; }
};

然后std::result_of_t<Foo()>Foo,并且专业化匹配!

但是如果我们有Foo=std::function<void()>,那么()的结果就是voidstd::result_of_t<Foo()>中出来。

因此,我们有is_callable<Foo, void>is_callable<Foo, Foo>匹配。这显然不匹配,因为Foo不等于void


template<class T, class U = void>
struct is_callable
// body unchanged

通知U=void而不是U=T

template<class T>
struct is_callable<T, std::void_t<std::result_of_t<T()>>>
// body unchanged

在这里我们使用void_t

std::void_t接受传递给它的任何类型,并产生void。现在让我们用is_callable< std::function<int()> >进行相同的练习。

在主要专业化下,这变成了

is_callable<std::function<int()>, void>

然后我们尝试将其与专业化匹配:

template<class T>
struct is_callable<T, std::void_t<std::result_of_t<T()>>>
再次

T=std::function<int()>立即到达。第二个子句在从属上下文中,因此我们不对它进行模式匹配。

struct is_callable<std::function<int()>, std::void_t<std::result_of_t<std::function<int()>()>>>

struct is_callable<std::function<int()>, std::void_t<int>>

struct is_callable<std::function<int()>, void>

哇,这与传递给模板的类型匹配!

std::enable_if使用了一个技巧,如果第一个参数为true,它将返回第二个参数(默认为void)。如果第一个参数为false,则为替换失败。

  

如何改进is_func不仅在可调用对象上返回true,而且在可构造的返回类型(可在std::is_constructible_v<处使用的情况下调用)

我将跳过尸体:

template<class T, class=void>
struct is_callable
// ...
template<class T>
struct is_callable<T, std::enable_if_t<
  std::is_constructible_v< std::result_of_t<T()> >
>>

现在,对于T不可构造的类型T(),专业化无法匹配,因为在计算第二个参数时会出现替换失败。并且当它可以构造时,我们得到void