Pregunta

Say I have

class Value;
class Key;

class MyClass {
  private:
  std::map<Key,Value> my_map;
  ....
}

Inside of MyClass methods I have a very convenient way to iterate through values of my_map by saying

 for( auto& value: my_map | boost::adaptors::map_values) {
    ...
 }

However I would like to have a method of MyClass that would essentially output my_map | boost::adaptors::map_values and allow convenient value iteration outside of MyClass methods. How do I declare such a method? Would I need to implement some sort of pseudo container and corresponding iterator or there is a shortcut?

¿Fue útil?

Solución

Basically, you have two good choices. You can provide users with a nicely decoupled interface by using boost's any_range, or else if you need the best performance, you can give clients direct access to the adapted range using c++11 decltypes. In the first case, clients won't have to change if you change implementation, but this comes at a cost of extra redirection.

using any_range:

#include <boost/range.hpp> 
#include <map> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/range/any_range.hpp>

class Foo 
{ 
    std::map<int, int> my_map;
    boost::any_range<int, boost::forward_traversal_tag, int&, std::ptrdiff_t> Values()
    { return my_map | boost::adaptors::map_values; }
}; 

giving direct access (awkward syntax here is because you require VS2013, which doesn't support member variables in unevaluated context at class scope):

#include <boost/range.hpp> 
#include <map> 
#include <boost/range/adaptor/map.hpp> 

class Foo 
{ 
    std::map<int, int> my_map;
    auto Values() -> decltype(my_map | boost::adaptors::map_values)
    { return my_map | boost::adaptors::map_values; }
}; 

Otros consejos

It isn't strictly necessary to use a pseudo container or an iterator adapter, such as boost::iterator_range. Although using a proper adapter such as iterator_range would theoretically be more correct and versatile, and will not violate the principle of least knowledge as applied to objects, you may want to avoid it because of an additional indirection, or maybe because your iterator is generally only a single-pass range and not a forward range.

So if you wish to use the adapted range directly you can simply use decltype to deduce the iterator type already returned by the adaptor:

#include <iostream>
#include <map>

#include <boost/range/adaptor/map.hpp>

class A {
  std::map<int,int> my_map = { {0, 1}, {2, 3} };
public:
  decltype(my_map | boost::adaptors::map_values)
  values() { return my_map | boost::adaptors::map_values; }
};

int main() {
    for (const auto& v : A().values())
        std::cout << "v = " << v << std::endl;
    return 0;
}
/* Output:
v = 1
v = 3
*/

And if you want the exposed values to be a const member function it's slightly more intricate:

class A { 
...
  decltype(const_cast<const std::map<int,int>&>(my_map) | boost::adaptors::map_values)
  values() const { return my_map | boost::adaptors::map_values; }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top