Как объявить адаптер диапазона увеличения (например, map_values)

Скажи у меня

class Value;
class Key;

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

Внутри методов MyClass у меня есть очень удобный способ перебирать значения my_map, говоря

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

Однако я хотел бы иметь метод MyClass, который по существу выводил бы
my_map | повышение :: адаптеры :: map_values
и разрешить удобную итерацию значений вне методов MyClass. Как мне объявить такой метод? Нужно ли реализовать какой-то псевдоконтейнер и соответствующий итератор или есть ярлык?

3

Решение

В принципе, у вас есть два хороших варианта. Вы можете предоставить пользователям красиво отделенный интерфейс, используя Boost’s any_rangeили, если вам нужна лучшая производительность, вы можете предоставить клиентам прямой доступ к адаптированному диапазону, используя c ++ 11 decltypes. В первом случае клиенты не должны будут меняться, если вы меняете реализацию, но это происходит за счет дополнительного перенаправления.

с помощью 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; }
};

предоставление прямого доступа (неудобный синтаксис здесь, потому что вам требуется VS2013, который не поддерживает переменные-члены в неоцененном контексте в области видимости класса):

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

Другие решения

Не обязательно использовать псевдоконтейнер или адаптер итератора, такой как boost :: iterator_range. Хотя использование подходящего адаптера, такого как iterator_range, теоретически будет более правильным и универсальным, и не будет нарушать принцип наименьшего знания применительно к объектам вы можете избежать этого из-за дополнительного косвенного обращения или из-за того, что ваш итератор, как правило, имеет только однопроходный диапазон, а не прямой диапазон.

Так что если вы хотите использовать адаптированный диапазон напрямую, вы можете просто использовать decltype чтобы вывести тип итератора, уже возвращенный адаптером:

#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
*/

И если вы хотите, чтобы отображаемые значения были константными функциями-членами, это немного сложнее:

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