我需要弄清楚模板参数是否是具有非无效返回值的可调用对象。
我已经定义了以下内容:
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<>
处使用)进行调用?
答案 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>
。将T
与Foo
进行匹配很容易;所以现在有了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()>
,那么()
的结果就是void
从std::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
。