了解类型是否可调用

Find out if type is callable

本文关键字:调用 是否 类型 了解      更新时间:2023-10-16

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

我定义了以下内容:

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<>(?

使用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。如果任一失败,则会发生替换失败,编译器将回退到一般情况。

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

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.