2017-04-24 3 views
2

Je récemment regardé ce discours sur C++: https://www.youtube.com/watch?v=mFUXNMfaciEgamme v3 aplatissant une séquence

Et je suis très intéressé à essayer. Donc, après un programme de jouets, je suis coincé sur la façon d'aplatir correctement un vecteur de vecteurs dans un vecteur. Selon la documentation ici: https://ericniebler.github.io/range-v3/ Ceci est possible en utilisant ranges::view::for_each. Cependant, je n'arrive pas à le faire fonctionner. Voici un code minimal.

#include <range/v3/all.hpp> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    auto nums = std::vector<std::vector<int>>{ 
     {0, 1, 2, 3}, 
     {5, 6, 7, 8}, 
     {10, 20}, 
     {30}, 
     {55} 
    }; 

    auto filtered = nums 
     | ranges::view::for_each([](std::vector<int> num) { return ranges::yield_from(num); }) 
     | ranges::view::remove_if([](int i) { return i % 2 == 1; }) 
     | ranges::view::transform([](int i) { return std::to_string(i); }); 

    for (const auto i : filtered) 
    { 
     std::cout << i << std::endl; 
    } 
} 
+0

Qu'est-ce qui ne fonctionne pas à ce sujet? – Galik

+0

@Galik une longue longue liste d'erreurs de compilation qui commencent par "Main.cpp: Dans l'instanciation de 'main() :: [avec auto: 1 = std :: vector ]':" –

Répondre

3

gamme v3 messages d'erreur ont tendance à être assez horrible, si bien que celui-ci est en fait mieux que la plupart:

 
prog.cc: In lambda function: 
prog.cc:16:90: error: no match for call to '(const ranges::v3::yield_from_fn) (std::vector<int>&)' 
     | ranges::view::for_each([](std::vector<int> num) { return ranges::yield_from(num); }) 
                         ^
In file included from /opt/wandbox/range-v3/include/range/v3/view.hpp:38:0, 
       from /opt/wandbox/range-v3/include/range/v3/all.hpp:21, 
       from prog.cc:1: 
/opt/wandbox/range-v3/include/range/v3/view/for_each.hpp:133:17: note: candidate: template<class Rng, int _concept_requires_132, typename std::enable_if<((_concept_requires_132 == 43) || ranges::v3::concepts::models<ranges::v3::concepts::View, T>()), int>::type <anonymous> > Rng ranges::v3::yield_from_fn::operator()(Rng) const 
      Rng operator()(Rng rng) const 
       ^~~~~~~~ 

à quelqu'un avec un peu de connaissance de l'émulation des concepts de gamme v3 couche, ce "clairement" indique que l'appel à yield_from a échoué parce que le type de l'argument que vous lui avez passé - std::vector<int> - ne satisfait pas le concept View. Le concept View caractérise un sous-ensemble de plages qui ne possèdent pas leurs éléments et qui ont donc toutes les opérations - déplacement/copie construction/affectation, début, fin et construction par défaut - calculables dans O (1). La gamme range algrebra dans range-v3 ne fonctionne que sur les vues pour éviter de devoir gérer les durées de vie des éléments et fournir des performances prévisibles.

yield_from rejette les std::vector s que vous essayez de passer, car ils ne sont pas vues, mais vous pouvez facilement fournir des vues (1) en prenant les vecteurs comme lvalues ​​plutôt que par valeur dans for_each, et (2) qui donne view::all de ceux lvalues ​​[DEMO]:

auto filtered = nums 
    | ranges::view::for_each([](std::vector<int>& num) { 
     return ranges::yield_from(ranges::view::all(num)); }) 
    | ranges::view::remove_if([](int i) { return i % 2 == 1; }) 
    | ranges::view::transform([](int i) { return std::to_string(i); }); 

Mais dans ce cas simple, aplatir une gamme de gammes d'éléments dans une série d'éléments a déjà une vue spécifique à but dans la gamme v3: view::join. Vous pouvez également utiliser cela [DEMO]:

auto filtered = nums 
    | ranges::view::join 
    | ranges::view::remove_if([](int i) { return i % 2 == 1; }) 
    | ranges::view::transform([](int i) { return std::to_string(i); }); 
+0

incroyable. Pouvez-vous recommander des documents ou des articles que je peux lire pour mieux comprendre comment tout cela fonctionne? –

+0

Aussi pourquoi yield_from ne fonctionne-t-il que sur des plages non-propriétaires alors que je ne peux pas être modifié dans 'ranges :: view_for_each()'? –

+1

Les vues acceptent les conteneurs * lvalue * et les convertissent implicitement en vues. L'idée est qu'une lvalue est susceptible de rester en vie jusqu'à ce que vous utilisiez le résultat de la composition de la vue alors qu'un rvalue - qui pourrait mourir à la fin de l'expression complète - ne l'est pas. 'yield_from' accepte les vues mais ne convertit pas implicitement lvalues ​​en vues, car son cas d'utilisation typique est dans une instruction return. Cela évite de renvoyer des vues qui référencent des conteneurs temporaires, c'est-à-dire exactement ce que fait le programme dans l'OP. – Casey