Итак, я хотел бы, чтобы все аргументы шаблона MemoizedFunctor автоматически выводились при построении. Я считаю, что это возможно, потому что в конструкции все аргументы шаблона однозначны.
Если я правильно понимаю, первый тип шаблона для 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
}
- РЕДАКТИРОВАТЬ -
Ашеплер в комментарии заметил, что в этом решении есть возможный недостаток: некоторые типы не могут быть возвращены из функции.
Например, функция не может вернуть массив в стиле C.
Это не проблема, определяющая TOut
(тип, возвращаемый operator()
) именно потому, что это тип, возвращаемый методом, и поэтому может быть возвращен также getOut()
.
Но это может быть (вообще говоря) проблемой для TIn
: если TIn
, например, int[4]
(не может быть, в этом случае, потому что используется в качестве ключа для неупорядоченной карты но, повторяю, вообще говоря), int[4]
не может быть возвращено getIn()
.
Вы можете обойти эту проблему (1), добавив структуру оболочки типа следующим образом
template <typename T>
struct typeWrapper
{ using type = T; };
(2) изменение getIn()
для возврата оболочки 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>;