2017-03-10 4 views
1

Je veux obtenir les éléments uniques d'un vector<foo> basé sur un membre de foo. J'utilise boost::adaptors::transform pour sélectionner le membre, puis le tri, puis en utilisant boost::adaptors::unique. J'ai du mal à faire marcher le tri. Laissant de côté l'appel unique pour l'instant, j'ai essayé le code ci-dessous sur .Comment boost :: range :: sort() un boost :: processed_range?

#include <iostream> 
#include <string> 
#include <vector> 
#include <boost/range/adaptor/transformed.hpp> 
#include <boost/range/algorithm/sort.hpp> 

struct foo 
{ 
    foo(std::string a) : bar(a) {} 

    std::string bar; 
    bool operator<(const foo& rhs) const {return bar < rhs.bar;} 
}; 

int main() 
{ 
    std::vector<foo> words = { foo("z"), foo("d"), foo("b"), foo("c") }; 

    #if 1 
    { 
     auto asString = boost::adaptors::transform(words, +[](const foo& x) {return x.bar;}); 
     auto sortedStrings = boost::range::sort(asString); 
     for (const auto& el : sortedStrings) 
      std::cout << el << std::endl; 
    } 
    #else 
    { 
     auto asString = boost::adaptors::transform(words, +[](const foo& x) {return x.bar;}); 
     std::sort(asString.begin().base(), asString.end().base()); 
     for (const auto& el : asString) 
      std::cout << el << std::endl; 
    } 

    { 
     auto sortedStrings = boost::range::sort(words); 
     for (const auto& el : sortedStrings) 
      std::cout << el.bar << std::endl; 
    } 
    #endif 


    return 0; 
} 

La section #if ne fonctionne pas:

In file included from /usr/local/include/c++/6.3.0/bits/char_traits.h:39:0, 
       from /usr/local/include/c++/6.3.0/ios:40, 
       from /usr/local/include/c++/6.3.0/ostream:38, 
       from /usr/local/include/c++/6.3.0/iostream:39, 
       from main.cpp:1: 
/usr/local/include/c++/6.3.0/bits/stl_algobase.h: In instantiation of 'void std::iter_swap(_ForwardIterator1, _ForwardIterator2) [with _ForwardIterator1 = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>; _ForwardIterator2 = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>]': 
/usr/local/include/c++/6.3.0/bits/stl_algo.h:84:20: required from 'void std::__move_median_to_first(_Iterator, _Iterator, _Iterator, _Iterator, _Compare) [with _Iterator = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' 
/usr/local/include/c++/6.3.0/bits/stl_algo.h:1918:34: required from '_RandomAccessIterator std::__unguarded_partition_pivot(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' 
/usr/local/include/c++/6.3.0/bits/stl_algo.h:1950:38: required from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size, _Compare) [with _RandomAccessIterator = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>; _Size = long int; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' 
/usr/local/include/c++/6.3.0/bits/stl_algo.h:1965:25: required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' 
/usr/local/include/c++/6.3.0/bits/stl_algo.h:4707:18: required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>]' 
/usr/local/include/boost/range/algorithm/sort.hpp:33:14: required from 'RandomAccessRange& boost::range::sort(RandomAccessRange&) [with RandomAccessRange = boost::range_detail::transformed_range<std::__cxx11::basic_string<char> (*)(const foo&), std::vector<foo> >]' 
main.cpp:27:57: required from here 
/usr/local/include/c++/6.3.0/bits/stl_algobase.h:148:11: error: no matching function for call to 'swap(boost::iterators::detail::iterator_facade_base<boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>, std::__cxx11::basic_string<char>, boost::iterators::random_access_traversal_tag, std::__cxx11::basic_string<char>, long int, false, false>::reference, boost::iterators::detail::iterator_facade_base<boost::iterators::transform_iterator<std::__cxx11::basic_string<char> (*)(const foo&), __gnu_cxx::__normal_iterator<foo*, std::vector<foo> >, boost::iterators::use_default, boost::iterators::use_default>, std::__cxx11::basic_string<char>, boost::iterators::random_access_traversal_tag, std::__cxx11::basic_string<char>, long int, false, false>::reference)' 
     swap(*__a, *__b); 

Alors que le std::sort avec le type de base iterator dans la section #else fonctionne. Je ne comprends pas pourquoi. Dans mon cas d'utilisation réel, j'utilise aussi boost::adaptors::indirect et boost::adaptors::filter (et je préférerais faire le filtrage avant de faire le tri, ou au moins essayer de le faire et voir comment ça fonctionne), c'est pourquoi je ne suis pas simplement trier words avec un prédicat lambda avant de faire la transformation.

+0

'boost :: swap (* asString.begin(), * asString.begin());' a échoué. – Jarod42

+0

Vous ne pouvez pas. Le tri nécessite un accès aléatoire. – sehe

+0

@sehe La documentation de transform() prétend qu'elle renvoie le même type de plage que celui qu'elle utilise, donc ce devrait être un accès aléatoire, n'est-ce pas? –

Répondre

2

Le problème est que vous avez une vue des chaînes temporaires que vous ne pouvez pas échanger. vous pouvez fixer votre code avec les éléments suivants:

auto asString = boost::adaptors::transform(words, +[](foo& x) -> std::string& {return x.bar;}); 

Demo

Notez que avec cela, vous triez la chaîne directement, et non la classe.

+0

Ah, cela a du sens que 'const foo & x' ne peut pas renvoyer une référence non-const à' bar'. Est-ce que le 'std :: string &' explicite est nécessaire? Il reviendra par valeur par défaut? Dans mon cas d'utilisation réel, j'ai découvert le problème de const pour une raison différente. Mon cas d'utilisation réel est de rendre le travail 'boost :: adapters :: unique' sur un vecteur d'itérateurs dans un ensemble , et bien sûr les chaînes de l'ensemble ne peuvent pas être permutées, donc l'approche' .base() 'peut ne fonctionne pas. Maintenant, je prends une copie du tri des itérateurs/unique à ce sujet. –

+0

@MattChambers: attention que 'sort' nécessite aussi des accès aléatoires ou des itérateurs, donc ne peut pas être utilisé avec la vue' filter' ou 'unique'. – Jarod42

+0

C'est génial, mais très improbable d'être ce dont les gens ont besoin, IYAM: http://coliru.stacked-crooked.com/a/31653bbd316a795b – sehe