我正在尝试编写函子备忘录,以节省重复执行昂贵函数的时间。在我的课堂设计中,我努力寻找一个简单的界面。
使用此Functor基类:
template <typename TOut, typename TIn>
class Functor {
public:
virtual
~Functor() {
}
virtual
TOut operator()(TIn input) = 0;
};
我现在想编写一个将封装和记忆函子的类。除了封装Functor
,MemoizedFunctor
本身还将是Functor
。这样就产生了3个模板参数。
这是一个有效的示例:
#include <unordered_map>
template <typename F, typename TOut, typename TIn>
class MemoizedFunctor : public Functor<TOut, TIn> {
public:
MemoizedFunctor(F f) : f_(f) {
}
virtual
~MemoizedFunctor() {
}
virtual
TOut operator()(TIn input) override {
if (cache_.count(input)) {
return cache_.at(input);
} else {
TOut output = f_(input);
cache_.insert({input, output});
return output;
}
}
private:
F f_;
std::unordered_map<TIn, TOut> cache_;
};
class YEqualsX : public Functor<double, double> {
public:
virtual
~YEqualsX() {
}
double operator()(double x) override {
return x;
}
};
int main() {
MemoizedFunctor<YEqualsX, double, double> f((YEqualsX())); // MVP
f(0); // First call
f(0); // Cached call
return 0;
}
我觉得必须有一种方法来消除必须指定所有3个模板参数的情况。给定函数传递给MemoizedFunctor
的构造函数后,我认为可以推导出所有三个模板参数。
我不确定如何重写该类,以便使用它不需要所有模板规范。
我尝试使用指向Functor的智能指针作为MemoizedFunctor
中的成员变量。这样就消除了第一个模板参数,但是现在该类的用户必须将智能指针传递给MemoizedFunctor
类。
总而言之,我希望在构造时自动推导出MemoizedFunctor
的所有模板参数。我相信这是可能的,因为在构造时,所有模板参数都是明确的。
答案 0 :(得分:4)
总而言之,我希望在构造时自动推导出MemoizedFunctor的所有模板参数。我相信这是可能的,因为在构造时,所有模板参数都是明确的。
如果我理解正确,MemoizedFunctor
的第一个模板类型就是Functor<TOut, TIn>
,或者是从某些Functior<TOut, TIn>
继承的东西,其中TOut
和{{1} }是TIn
的第二和第三模板参数。
在我看来,您正在寻找扣除指南。
为了推断第二个和第三个模板参数,我建议声明以下几对函数(无需定义,因为仅在MemoizedFunctor
中使用)
decltype()
现在,使用template <typename TOut, typename TIn>
constexpr TIn getIn (Functor<TOut, TIn> const &);
template <typename TOut, typename TIn>
constexpr TOut getOut (Functor<TOut, TIn> const &);
和decltype()
,用户定义的推导指南就变成了
std::declval()
以下是完整的编译示例
template <typename F>
MemoizedFunctor(F)
-> MemoizedFunctor<F,
decltype(getOut(std::declval<F>())),
decltype(getIn(std::declval<F>()))>;
-编辑-
Aschepler在评论中指出,此解决方案可能存在一个缺点:某些类型无法从函数中返回。
通过示例,函数无法返回C样式的数组。
确切地推论#include <unordered_map>
template <typename TOut, typename Tin>
class Functor
{
public:
virtual ~Functor ()
{ }
virtual TOut operator() (Tin input) = 0;
};
template <typename TOut, typename TIn>
constexpr TIn getIn (Functor<TOut, TIn> const &);
template <typename TOut, typename TIn>
constexpr TOut getOut (Functor<TOut, TIn> const &);
template <typename F, typename TOut, typename TIn>
class MemoizedFunctor : public Functor<TOut, TIn>
{
public:
MemoizedFunctor(F f) : f_{f}
{ }
virtual ~MemoizedFunctor ()
{ }
virtual TOut operator() (TIn input) override
{
if ( cache_.count(input) )
return cache_.at(input);
else
{
TOut output = f_(input);
cache_.insert({input, output});
return output;
}
}
private:
F f_;
std::unordered_map<TIn, TOut> cache_;
};
class YEqualsX : public Functor<double, double>
{
public:
virtual ~YEqualsX ()
{ }
double operator() (double x) override
{ return x; }
};
template <typename F>
MemoizedFunctor(F)
-> MemoizedFunctor<F,
decltype(getOut(std::declval<F>())),
decltype(getIn(std::declval<F>()))>;
int main ()
{
MemoizedFunctor f{YEqualsX{}};
f(0); // First call
f(0); // Cached call
}
(由TOut
返回的类型)并不是一个问题,因为它是方法返回的类型,因此operator()
也可以返回。
但这通常是getOut()
的问题:例如,如果TIn
是TIn
(在这种情况下就不能用,因为一个无序映射的键,但是,一般来说,我要重复),int[4]
不能返回int[4]
。
您可以解决此问题(1)如下添加类型包装器结构
getIn()
(2)修改template <typename T>
struct typeWrapper
{ using type = T; };
以返回包装器getIn()
TIn
和(3)修改推导指南以从包装器中提取template <typename TOut, typename TIn>
constexpr typeWrapper<TIn> getIn (Functor<TOut, TIn> const &);
TIn
答案 1 :(得分:3)
在:
template <typename F, typename TOut, typename TIn>
class MemoizedFunctor : public Functor<TOut, TIn> { ... }
如果F
必须是Functor
的实现(我猜是这样吗?),则可以为Functor
添加一些别名使您的生活更轻松(这些操作可以在外部而不是内部进行,但使这种侵入性似乎是合理的):
template <typename TOut, typename TIn>
class Functor {
public:
using in_param = TIn;
using out_param = TOut;
// ... rest as before ...
};
然后更改MemoizedFunctor
以直接使用这些别名。您真的没有独立的模板参数,它们是完全依赖的吧?
template <typename F>
class MemoizedFunctor : public Functor<typename F::out_param, typename F::in_param> { ... }
有了这一更改(并同样更改了TOut
和TIn
的内部用法),此操作就可以按需进行了(当然,因为我们现在只有一个模板参数,因此只能提供一个模板参数) ):
MemoizedFunctor<YEqualsX> f(YEqualsX{});
该参数可以直接通过CTAD推导而无需任何进一步更改
MemoizedFunctor f(YEqualsX{});