Выведение типа шаблона из аргумента функции - PullRequest
3 голосов
/ 23 апреля 2019

Я не уверен насчет названия вопроса, но в основном мне интересно, как создать функцию, похожую на посетителя, которая может работать с определенными типами в коллекции, которая правильно использует вывод типов.

Например, коллекция содержит объекты, которые наследуются от одного базового класса (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 & 'from' {.... } ::

Можно ли определить шаблон, который может выводить типы таким образом, или спецификация языка не позволяет это сделать?

Ответы [ 3 ]

1 голос
/ 23 апреля 2019

Вы пометили 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.

1 голос
/ 23 апреля 2019

Я нашел самый простой способ сделать это до сих пор (но не совсем то, что я искал) - определить посетителя с помощью второй формы, указанной в вопросе, и объявить параметр lambda как * 1001. *:

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>([](auto f) { f->some_method(); });
0 голосов
/ 23 апреля 2019

Мы можем добиться желаемого синтаксиса 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());
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...