从函数参数推断模板类型

时间:2019-04-23 11:24:59

标签: c++ templates c++17 type-inference

我不确定问题的标题,但基本上我很好奇如何创建一个类似访问者的函数,该函数可以在正确使用类型推断的集合上对某些类型进行操作。

例如,一个集合包含从单个基类(Base)继承的对象。某些操作仅适用于特定的子类(例如FooBar继承自Base)。

一个实现可以是

template<class T, class F>
void visit(F f)
{
  for (auto c : the_collection) {
    if (auto t = dynamic_cast<T*>(c)) {
      f(t);
    }
  }
}

这里的问题是,调用此类函数将需要两次指定类类型FooBar

visit<FooBar>([](FooBar* f) { f->some_method(); });

我想使用类型推断,因此我只能编写visit([](FooBar* f) ...,但无法获得正确的模板。

例如:

template<class T>
using Visitor = std::function<void(T*)>;
template<class T>
void visit(const Visitor<T>& f)
{
  for (auto c : the_collection)
    if (auto t = dynamic_cast<T*>(c))
      f(t);
}

可与visit<FooBar>([](FooBar*) ...一起使用,但不能与visit([](FooBar*) ...一起使用。

  

找不到匹配的重载函数

     

void visit(const std :: function &)':无法从'{....推论出'const std :: function &'的模板参数。 } ::

是否可以定义一个可以以此方式推断类型的模板,或者语言规范不允许这样做?

3 个答案:

答案 0 :(得分:1)

到目前为止,我发现最简单的方法(但并不是我一直在寻找的)是使用问题中指出的第二种形式定义访问者,并将lambda参数声明为{{ 1}}:

auto

答案 1 :(得分:1)

您标记了C ++ 17,因此可以对std::function使用推导法。

那下面的事情呢?

template <typename>
struct first_arg_type;

template <typename R, typename T0, typename ... Ts>
struct first_arg_type<std::function<R(T0, Ts...)>>
 { using type = T0; };

template <typename F>
void visit (F const & f)
 {
   using T = typename first_arg_type<decltype(std::function{f})>::type;

   for (auto c : the_collection)
      if (auto t = dynamic_cast<T>(c))
         f(t);
 }

请注意,您可以在first_arg_type中使用标准类型first_argument_type来代替自定义类型特征std::function

   using T = typename decltype(std::function{f})::first_argument_type;

不幸的是,std::function::first_argument_type从C ++ 17开始不推荐使用,将从C ++ 20中删除。

答案 2 :(得分:0)

我们可以通过推导lambda的调用运算符的类型来实现visit([](FooBar*){ /*...*/ });所需的语法。

template <typename Element, typename Class, typename Parameter>
void call(Element element, Class *callable, void(Class::*function)(Parameter) const) {
  if (auto parameter = dynamic_cast<Parameter>(element)) {
    (callable->function)(parameter);
  }
}

template <typename Functor>
void visit(Functor &&functor) {
  for (auto element : the_collection) {
    call(element, &functor, &Functor::operator());
  }
}