假设我有bar
和foo
类型。如何构建模板类has_call_with_arg<>
,使得当{且仅当
has_call_with_arg<bar,foo>::value
为真
bar b;
foo f;
b(f);
会编译吗?我查看了各种相关问题(包括上面提到的)并尝试了
template<typename Func, typename Arg>
class has_call_with_arg
{
struct bad {};
struct test : Func
{
template<typename C>
bad operator()(C const&r);
};
public:
static const bool value =
!std::is_same<bad, typename std::result_of<test(Arg const&)>::type >::value;
};
但是没有用(没有检测到正确的匹配)。怎么了?
答案 0 :(得分:3)
template<typename sig, typename functor> struct is_callable;
template<typename Ret, typename... Arg, typename functor>
struct is_callable<Ret(Arg...), functor> { // partial spec
private:
struct no {};
public:
template<typename U> static auto f(std::nullptr_t) -> decltype(std::declval<U>()(std::declval<Arg>()...));
template<typename U> static no f(...);
static const int value = std::is_convertible<decltype(f<functor>(nullptr)), Ret>::value;
};
我创建了这个内容for my tutorials,它解释了这个特征的构造(首先是非可变形式)。
答案 1 :(得分:2)
std::result_of<test(Arg const&)>
提供模板参数,该参数是引用Arg const
并返回test
的函数类型。所以它的type
是test
,不太有帮助。
请注意,如上所述,需要代码
bar b;
foo f;
b(f);
有效是5个要求:bar
和foo
每个都有一个可访问的默认构造函数和析构函数,b(f)
是一个有效的表达式。我将专注于最后一个(这可能是你的意思)。如果您确实是指其他部分,则可以使用标准<type_traits>
属性添加这些部分。
函数std::declval
非常适合假装你有一个给定类型的对象,即使你没有。它绝不能被调用,所以它通常只在decltype
表达式中使用。
有两种基本的方法可以实现SFINAE技巧,基于两个地方C ++允许模板参数推断失败,只是丢弃失败的声明:
首先,尝试匹配类部分特化:
template<typename Func, typename Arg, typename Enable = void>
struct has_call_with_arg1 : public std::false_type {};
template<typename Func, typename Arg>
struct has_call_with_arg1<Func, Arg,
decltype(std::declval<Func&>()(std::declval<Arg&>()))>
: public std::true_type {};
其次,当至少一个重载是函数模板时,重载解析。 (类模板的非模板成员函数在这里不起作用,因为实例化类需要每个成员声明都有效。)
namespace has_call_with_arg_impl {
template<typename F, typename A>
std::true_type test(decltype(std::declval<F&>()(std::declval<A&>()))*);
template<typename F, typename A>
std::false_type test(...);
}
template <typename Func, typename Arg>
struct has_call_with_arg2
: public decltype(has_call_with_arg_impl::test<Func,Arg>(nullptr)) {};