我想编写C ++类(让类型名称为Adapter),它在构造函数中接受函数指针或函数对象(特别是lambda函数)并在内部按值存储它(如std :: function for为例)。在构造类之后,它也表示功能对象,并且作为内部功能对象的包装器。但是有以下条件:
第一个问题 - 它可能与C ++ 11一起使用,第二个问题我怎么能这样做?我不知道我应该从哪里开始。
我需要推断(在模板中)函数对象的返回值吗?第一个陷阱是,std :: result_of不支持函数指针。我不知道如何推断功能对象的参数类型(可能是空的,没有参数或ArgType)(我知道函数指针是如何通过模板化结构实现的,但我怎样才能为函数对象做到这一点) ?)。
我需要这样的东西:
template <typename Functor?> class Adapter
{
private:
std::function<Void_or_ReturnType (Void_or_ArgType)> f;
public:
template <typename Functor?> Adapter(const Functor& f) { ??? }
ReturnType operator()(ArgType arg)
{
// one of the following four variants:
return f(arg);
f(arg); return DefaultValue;
return f();
f(); return DefaultValue;
}
};
可能需要在模板中推导出Void_or_ReturnType和Void_or_ArgType类型,并为四种情况中的每一种为operator()编写不同的模板特化。但我怎么能这样做呢?
答案 0 :(得分:0)
我花了几个小时就找到了一些解决方案: http://coliru.stacked-crooked.com/a/628b8971466fa74d
不确定,如果这是一个好的解决方案。可能有人建议我,我怎样才能最小化这段代码。
我在这里复制源代码以防万一(我在coliru.stacked-crooked.com上更新了源码,这里是旧版本,请看上面的链接):
#include <functional>
#include <stdio.h>
// functors
template<class T, class Yes, class No> struct IsArgs { typedef typename IsArgs <decltype(&T::operator()), Yes, No>::type type; };
template<class T, class Yes, class No> struct IsResult { typedef typename IsResult<decltype(&T::operator()), Yes, No>::type type; };
// function pointers
template<class R, class Yes, class No, typename ...Args> struct IsArgs <R (*)(Args...), Yes, No> { typedef Yes type; };
template<class R, class Yes, class No> struct IsArgs <R (*)(), Yes, No> { typedef No type; };
template<class Yes, class No, class R, typename ...Args> struct IsResult <R (*)(Args...), Yes, No> { typedef Yes type; };
template<class Yes, class No, typename ...Args> struct IsResult <void (*)(Args...), Yes, No> { typedef No type; };
// for function refs
template<class R, class Yes, class No, typename ...Args> struct IsArgs <R (Args...), Yes, No> { typedef Yes type; };
template<class R, class Yes, class No> struct IsArgs <R (), Yes, No> { typedef No type; };
template<class Yes, class No, typename R, typename ...Args> struct IsResult <R (Args...), Yes, No> { typedef Yes type; };
template<class Yes, class No, typename ...Args> struct IsResult <void (Args...), Yes, No> { typedef No type; };
// for member pointers (lambdas, functors)
template<class T, class Yes, class No, class R, typename ...Args> struct IsArgs <R (T::*)(Args...) const, Yes, No> { typedef Yes type; };
template<class T, class Yes, class No, class R> struct IsArgs <R (T::*)() const, Yes, No> { typedef No type; };
template<class T, class Yes, class No, class R, typename ...Args> struct IsResult <R (T::*)(Args...) const, Yes, No> { typedef Yes type; };
template<class T, class Yes, class No, typename ...Args> struct IsResult <void (T::*)(Args...) const, Yes, No> { typedef No type; };
template <typename Retval, Retval Default, typename ...Args> class Adapter
{
struct Noargs {
std::function<Retval ()> func;
template <typename F> Noargs(const F& f) : func(f) {}
Retval operator()(Args...) { return func(); }
};
struct Noreturn {
std::function<void (Args...)> func;
template <typename F> Noreturn(const F& f) : func(f) {}
Retval operator()(Args...args) { return func(args...), Default; }
};
struct Noretargs {
std::function<void ()> func;
template <typename F> Noretargs(const F& f) : func(f) {}
Retval operator()(Args...) { return func(), Default; }
};
std::function<Retval (Args...args)> func;
public:
template <typename Functor> Adapter(const Functor &f)
: func(typename IsArgs<Functor,
typename IsResult<Functor, std::function<Retval (Args...)>, Noreturn>::type,
typename IsResult<Functor, Noargs, Noretargs>::type>::type(f)) {}
Retval operator()(Args...args) const { return func(args...); }
};
void deaf_silent_f() { puts("deaf/silent"); }
void silent_f(const char *arg) { printf("silent %s\n", arg); }
const char *deaf_f() { puts("deaf"); return "deaf"; }
const char *normal_f(const char *arg) { printf("normal %s\n", arg); return "normal"; }
const char Default[] = "DEFAULT";
int main(int argc, char *argv[])
{
typedef Adapter<const char*, Default, const char*> A;
{
puts("function refs");
A ds(deaf_silent_f); printf("-> %s\n", ds("ds"));
A s(silent_f); printf("-> %s\n", s("s"));
A d(deaf_f); printf("-> %s\n", d("d"));
A n(normal_f); printf("-> %s\n", n("n"));
puts("");
}
{
puts("function pointers");
A ds(&deaf_silent_f); printf("-> %s\n", ds("ds"));
A s(&silent_f); printf("-> %s\n", s("s"));
A d(&deaf_f); printf("-> %s\n", d("d"));
A n(&normal_f); printf("-> %s\n", n("n"));
puts("");
}
{
puts("functors");
A ds([=]{ printf("deaf/silent %d\n", argc); }); printf("-> %s\n", ds("ds"));
A s([=](const char *a){ printf("silent(%s) %d\n", a, argc); }); printf("-> %s\n", s("s"));
A d([=]{ printf("deaf %d\n", argc); return "deaf"; }); printf("-> %s\n", d("d"));
A n([=](const char *a){ printf("normal(%s) %d\n", a, argc); return "normal";}); printf("-> %s\n", n("n"));
}
return 0;
}
答案 1 :(得分:0)
嗯......你问的问题并非琐碎......
首先:我认为将默认返回值作为模板参数是一个好主意:适用于整数类型,但是,例如,不适用于浮点类型。你可以解决这个问题,但我建议在构造函数中将默认值作为值传递。
第二:我建议使用以下代码来检测F
类型的可调用对象是否可以使用给定的类型列表进行调用
template <typename ...>
constexpr std::false_type isInvocableWithHelper (long);
template <typename F, typename ... Args>
constexpr auto isInvocableWithHelper (int)
-> decltype( std::declval<F>()
(std::forward<Args>(std::declval<Args>())...),
std::true_type{} );
template <typename F, typename ... Args>
using isInvocableWith = decltype(isInvocableWithHelper<F, Args...>(0));
现在,使用委托构造函数和标记分派,您的Adapter
构造函数可以
template <typename F>
Adapter (F const & f, RetT defVal = RetT{})
: Adapter{f, defVal, isInvocableWith<F, Args...>{},
isInvocableWith<F>{}}
{ }
其中第三个参数(isInvocableWith<F, Args...>{}
)为真(std::true_type
)仅当F
对象可以使用Args...
个对象列表和第四个参数({{仅当isInvocableWith<F>{}
对象在没有参数的情况下可调用时,{1}})才为真(std::true_type
)。
现在您必须检测F
(类型为F
的对象)是否返回F
或值(可能是void
或可转换为RetT
的内容})。
您可以再次使用委托构造函数和使用
进行标记分派RetT
如果可以使用 template <typename F>
Adapter (F const & f, RetT const & defVal, std::true_type const &,
std::false_type const &)
: Adapter{f, defVal, std::true_type{}, std::false_type{},
std::is_same<void,
decltype(f(std::forward<Args>(std::declval<Args>())...))>{}}
{ }
参数进行调用(如果Args...
返回std::true_type
,则观察最后一个参数是std::true_type
(继承自f
)。< / p>
如果void
无参数可调用,则构造函数变为
f
现在是四个最终的构造函数
template <typename F>
Adapter (F const & f, RetT const & defVal, std::false_type const &,
std::true_type const &)
: Adapter{f, defVal, std::false_type{}, std::true_type{},
std::is_same<void, decltype(f())>{}}
{ }
观察到,在所有情况下,我都构建了一个lambda函数,它接收 template <typename F>
Adapter (F const & f, RetT const & defVal, std::true_type const &,
std::false_type const &, std::true_type const &)
: defV{defVal}
{
func = [&, this](Args && ... as)
{ f(std::forward<Args>(as)...); return defV; };
std::cout << "--- case 1 (full, void)" << std::endl;
}
template <typename F>
Adapter (F const & f, RetT const &, std::true_type const &,
std::false_type const &, std::false_type const &)
{
func = [&](Args && ... as)
{ return f(std::forward<Args>(as)...); };
std::cout << "--- case 2 (full, RetT)" << std::endl;
}
template <typename F>
Adapter (F const & f, RetT const & defVal, std::false_type const &,
std::true_type const &, std::true_type const &)
: defV{defVal}
{
func = [&, this](Args && ...)
{ f(); return defV; };
std::cout << "--- case 3 (noArgs, void)" << std::endl;
}
template <typename F>
Adapter (F const & f, RetT const &, std::false_type const &,
std::true_type const &, std::false_type const &)
{
func = [&](Args && ...)
{ return f(); };
std::cout << "--- case 4 (noArgs, RetT)" << std::endl;
}
个参数列表并仅在适当时使用它。
我理解这有点复杂,但以下是根据此解决方案修改的答案中的示例。
Args...