lundi 5 mai 2014

Lambdas polymorphiques

G++4.9 vient de débarquer dans Debian Jessie. C'est donc l'occasion de voir ce que cette nouvelle mouture nous réserve, en particulier du côté du support de C++14. Les lambdas polymorphiques sont similaires aux fonctions templates, les paramètres génériques (spécifiés par auto) étant résolus à l'instanciation de la fonction. Voici un petit exemple:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>


int main()
{
  std::vector<int> v = {1, 2, 3, 4, 5};
  
  std::for_each(v.begin(),
                v.end(),
                [](auto i) { std::cout << i << std::endl; }
                );
  
  auto output = std::accumulate(
                  v.begin(),
                  v.end(),
                  std::string(" == > "),
                  [](auto & acc, auto i) 
                    { return acc + std::to_string(i); }
                  );

  std::cout << output << std::endl;
  
  return 0;
}

Dans l'exemple précédent, la fonction lambda est instanciée au moment où elle est déclarée, mais l'on n'est pas obligé d'effectuer ces opérations ensemble, si l'on veut réutiliser sa lambda. Voyez l'exemple suivant: la lambda est créée (son type est auto, puisqu'il n'est pas possible de déclarer une lambda autrement qu'en la définissant), et elle est utilisée (et donc instanciée) à deux endroits.

#include <iostream>
#include <vector>
#include <algorithm>


int main()
{
  auto turn_to_pair = [](auto & left, auto & right) 
    { return std::make_pair(left, right); };
  
  auto l1 = {1, 2, 3, 4, 5};
  auto l2 = {"a", "b", "c", "d", "e"};

  std::vector<std::pair<int, std::string> > r1;
  std::vector<std::pair<std::string, int> > r2;

  std::transform(l1.begin(),
                 l1.end(),
                 l2.begin(),
                 std::back_inserter(r1),
                 turn_to_pair);

  std::transform(l2.begin(),
                 l2.end(),
                 l1.begin(),
                 std::back_inserter(r2),
                 turn_to_pair);

  return 0;
}

Au passage, si j'ai bien tout compris au post de Scott Meyers, ma lambda pourrait être encore plus générique en prenant des références universelles. Après avoir longuement cherché, je n'ai toujours pas trouvé s'il était nécessaire d'utiliser le perfect forwarding si la fonction appelée attend elle-même des références universelles, mais ça ne peut pas faire de mal. L'on écrirait alors:

  auto turn_to_pair = [](auto && left, auto && right) 
    { return std::make_pair(std::forward(left), 
                            std::forward(right)); };

Aucun commentaire: