Как я могу передать лямбда (c ++ 11) в шаблонную функцию? - PullRequest
3 голосов
/ 31 октября 2011

Я работаю с лямбда-функциями в gcc 4.6.2 и хотел бы реализовать шаблонную функцию "map", например:

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) {
  std::vector<B> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

Это не работает, потому что тестовый код:

int main(int argc, char **argv) {
  std::vector<int> list;
  list.push_back(10);
  list.push_back(20);
  list.push_back(50);

  std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; });
  std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); });
  return 0;
}

выдает эту ошибку:

test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’
test.cpp:49:80: note: candidate is:
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>)

Если я удаляю шаблон и использую вектор напрямую, он прекрасно компилируется:

std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) {
  std::vector<int> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

так что, должно быть, проблема с тем, как я определяю шаблон.

Кто-нибудь сталкивался с этим раньше? Я знаю, что лямбды невероятно новы.

Ответы [ 3 ]

2 голосов
/ 31 октября 2011

Вам не нужно использовать std :: function. Просто сделайте параметр предиката значением шаблона. Например,

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) {

std :: function <> более полезен в качестве типа значения элемента или для определения не шаблонного кода.

0 голосов
/ 31 октября 2011

Проблема в том, что компилятор не может понять, что использовать для B. Чтобы определить этот тип, он хочет использовать функцию <>, которую вы передаете для f, но вы не передаете std:: функция <> напрямую.Вы передаете что-то, что ожидаете использовать для создания функции <>.И чтобы сделать это неявное построение, ему нужно знать тип аргумента.Таким образом, у вас есть циклическая зависимость, в которой тип аргумента зависит от того, что вы передаете, но то, что передается, зависит от типа аргумента.

Вы можете разорвать эту циклическую зависимость, указав параметры шаблона,например, map_<int,int>(list, [](int x) -> char { return x + 1; });

(хотя я вижу, что функтор на самом деле возвращает символ, а не целое число, так что если здесь у вас сработало выведение типа, вы получите обратно vector<char>, который не может быть преобразован вvector<int> когда вы присваиваете результат transformed)

Однако, как уже было отмечено, обычно шаблоны воспринимают функторы как простой тип шаблона:

template<typename A,typename Func>
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> {
    std::vector<decltype(f(A()))> rv;
    /*...*/
}

(мы используемконечный тип возвращаемого значения, потому что нам нужно использовать выражение f в возвращаемом типе, который недоступен, если только возвращаемый тип не будет получен позднее.)

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

Также принято использовать итераторы в качестве аргументов для такого рода забавctions, в этом случае ваша функция является просто оберткой вокруг std :: transform, так что вы можете просто использовать это напрямую.Я не уверен, что есть особая ценность в специальной версии, которая имеет дело с векторами.

0 голосов
/ 31 октября 2011

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

#include <iostream>

#include <vector>

#include <algorithm>

#include <iterator>



template <typename T,typename C>

struct map  {

   typedef C (*F)(const T&);

   std::vector<C> rv;

   map () {}

   map (const std::vector<T>& o,F f)  {

      rv.resize(o.size());

      std::transform (o.begin(),o.end(),rv.begin(),f);

    }

   ~map () {}

   operator std::vector<C> () const  {

      return rv;

    }

 };



int main ()  {

   std::vector<int> asd(5,12);

   std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;});

   std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," "));

 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...