>我需要弄清楚模板参数是否是具有非 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>
. 将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
.