Class 模板派生自另一个 Class 模板类型推导
Class Template Derived from Another Class Template Type Deduction
我正在尝试编写仿函数记忆器以节省重复昂贵的函数调用的时间。在我的 class 设计中,我正在努力寻找一个简单的界面。
使用这个 Functor 基数 class:
template <typename TOut, typename TIn>
class Functor {
public:
virtual
~Functor() {
}
virtual
TOut operator()(TIn input) = 0;
};
我现在想写一个 class 来封装和记忆一个仿函数。除了封装一个 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
的构造函数的函数,我认为可以推导出所有三个模板参数。
我不确定如何重写 class 以便使用它不需要所有模板规范。
我尝试使用指向 Functor 的智能指针作为 MemoizedFunctor
中的成员变量。这消除了第一个模板参数,但现在 class 的用户必须将智能指针传递给 MemoizedFunctor
class.
总而言之,我希望 MemoizedFunctor
的所有模板参数在构造时自动推导。我相信这是可能的,因为在构建时所有模板参数都是明确的。
In summary, I would like to have all the template arguments of MemoizedFunctor be automatically be deduced on construction. I believe this is possible because on construction all template arguments are unambiguous.
如果我理解正确,MemoizedFunctor
的第一个模板类型永远是 Functor<TOut, TIn>
,或者是从某些 Functior<TOut, TIn>
继承的东西,其中 TOut
和 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>()))>;
下面是一个完整的编译示例
#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
}
-- 编辑 --
Aschepler 在评论中指出,此解决方案可能存在一个缺点:无法从函数 return 编辑某些类型。
例如,函数不能 return 一个 C-style 数组。
推导 TOut
(由 operator()
编辑的类型 return)这不是一个问题,因为 return 类型是由方法编辑的 return 所以 return也可以通过 getOut()
.
但这可能(一般来说)是 TIn
的问题:如果 TIn
例如 int[4]
(在这种情况下不可能,因为是用作无序映射的键,但我重复一遍,一般来说),int[4]
不能 return 由 getIn()
编辑。
您可以绕过这个问题 (1) 添加类型包装器结构,如下所示
template <typename T>
struct typeWrapper
{ using type = T; };
(2) 将 getIn()
修改为 return 包装器 TIn
template <typename TOut, typename TIn>
constexpr typeWrapper<TIn> getIn (Functor<TOut, TIn> const &);
和(3)修改推导指南以从包装器
中提取TIn
template <typename F>
MemoizedFunctor(F)
-> MemoizedFunctor<F,
decltype(getOut(std::declval<F>())),
typename decltype(getIn(std::declval<F>()))::type>;
在:
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{});
我正在尝试编写仿函数记忆器以节省重复昂贵的函数调用的时间。在我的 class 设计中,我正在努力寻找一个简单的界面。
使用这个 Functor 基数 class:
template <typename TOut, typename TIn>
class Functor {
public:
virtual
~Functor() {
}
virtual
TOut operator()(TIn input) = 0;
};
我现在想写一个 class 来封装和记忆一个仿函数。除了封装一个 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
的构造函数的函数,我认为可以推导出所有三个模板参数。
我不确定如何重写 class 以便使用它不需要所有模板规范。
我尝试使用指向 Functor 的智能指针作为 MemoizedFunctor
中的成员变量。这消除了第一个模板参数,但现在 class 的用户必须将智能指针传递给 MemoizedFunctor
class.
总而言之,我希望 MemoizedFunctor
的所有模板参数在构造时自动推导。我相信这是可能的,因为在构建时所有模板参数都是明确的。
In summary, I would like to have all the template arguments of MemoizedFunctor be automatically be deduced on construction. I believe this is possible because on construction all template arguments are unambiguous.
如果我理解正确,MemoizedFunctor
的第一个模板类型永远是 Functor<TOut, TIn>
,或者是从某些 Functior<TOut, TIn>
继承的东西,其中 TOut
和 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>()))>;
下面是一个完整的编译示例
#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
}
-- 编辑 --
Aschepler 在评论中指出,此解决方案可能存在一个缺点:无法从函数 return 编辑某些类型。
例如,函数不能 return 一个 C-style 数组。
推导 TOut
(由 operator()
编辑的类型 return)这不是一个问题,因为 return 类型是由方法编辑的 return 所以 return也可以通过 getOut()
.
但这可能(一般来说)是 TIn
的问题:如果 TIn
例如 int[4]
(在这种情况下不可能,因为是用作无序映射的键,但我重复一遍,一般来说),int[4]
不能 return 由 getIn()
编辑。
您可以绕过这个问题 (1) 添加类型包装器结构,如下所示
template <typename T>
struct typeWrapper
{ using type = T; };
(2) 将 getIn()
修改为 return 包装器 TIn
template <typename TOut, typename TIn>
constexpr typeWrapper<TIn> getIn (Functor<TOut, TIn> const &);
和(3)修改推导指南以从包装器
中提取TIn
template <typename F>
MemoizedFunctor(F)
-> MemoizedFunctor<F,
decltype(getOut(std::declval<F>())),
typename decltype(getIn(std::declval<F>()))::type>;
在:
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{});