문제

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?

도움이 되었습니까?

해결책

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; }
}; 

다른 팁

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; }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top