覆盖C++ map迭代器解引用操作

3
我有一个包装C++ map的类。我希望仅覆盖迭代器解引用操作,以只返回map的值而不是键。这是否可能,而不必重新实现整个std::map迭代器(尽可能避免这样做)?以下是代码:
#include <map>

using std::map;

class X {
    using Type = map<int, double>;
    using const_iterator = typename Type::const_iterator;

  public:

    void insert(int key, double value) {
        my_map[key] = value;
    }

    const_iterator cbegin() const { return my_map.cbegin(); }

    const_iterator cend() const { return my_map.cend(); }

    const_iterator begin() const { return my_map.cbegin(); }

    const_iterator end() const { return my_map.cend(); }

  private:
    Type my_map;
};

int main() {
    X x;
    double i;
    for (const auto& it : x) {
        i = it.second; // works
        i = it; // fails
    }
}

void insert(int key, double value) { -- map::insertmap::operator [] 之间有很大的区别。你的包装器通过只向调用者提供 [ ] 来混淆两者。 - PaulMcKenzie
@PaulMcKenzie 我知道,谢谢你指出来,但那不是重点。这只是一个简单的类,用于演示我正在寻找的内容。插入操作与我的真实类别无关。 - darksky
评论区是用来发表评论的,所以我留了言。 - PaulMcKenzie
2个回答

1
您需要实现一个完整的迭代器类型来提供新的迭代器行为。幸运的是,Boost有一些工具可以使这个过程更容易:boost::iterator_facade是一个用于创建迭代器类型的工具,它负责满足标准定义的各种Iterator类型的所有要求细节。对于常见情况,即想要创建包装另一个迭代器、覆盖其功能的部分的迭代器,可以使用boost::iterator_adaptor
因此,您可以像这样定义X::const_iterator
#include <map>
#include <boost/iterator_adaptor.hpp>

using std::map;

class X {
    using Type = map<int, double>;
public:
    class const_iterator : public boost::iterator_adaptor<
        const_iterator,                 // Derived iterator type, for CRTP
        typename Type::const_iterator,  // Wrapped iterator type
        const double>                   // Value type
    {
    public:
        const_iterator() {}
    private:
        // Allow X to create X::const_iterator from Type::const_iterator:
        explicit const_iterator(typename Type::const_iterator map_iter)
            : iterator_adaptor(map_iter) {}
        friend X;

        // Define the dereference operation:
        const double& dereference() const
        { return base()->second; }
        // Allow boost's internals to use dereference():
        friend boost::iterator_core_access;
    };

    const_iterator cbegin() const { return const_iterator(my_map.cbegin()); }
};

...

我有意将名称为X::const_iterator的访问权限从私有更改为公共。某些人可能希望明确命名该迭代器类型。


Boost有一个范围适配器可以实现这个功能。迭代器类型将是boost::adaptors::select_second_const_range<Type>::iterator - Caleth
@Caleth 你有使用它的例子吗?我似乎找不到一个例子。 - darksky
1
@darksky 我会倾向于只定义一个 auto values() const { return my_map | boost::adaptors::map_values; },然后你可以这样使用 for (const auto & d : x.values()) { ... },但你也可以使用数据成员。 - Caleth
是这样的,我真的很希望它能用 cbegincend 实现,这样我的使用 map 的类就可以直接对 X 类进行 for-range 循环,而无需调用 values() 或其他什么东西。 - darksky

0
借鉴Yakk的答案这里,您可以轻松地修改它以适应您的需求。
template<class T>
T value_of(T t) { return std::move(t); }
template<class K, class V>
V value_of(std::pair<K, V> const& p) { return p.second; }

template<class It>
struct range_t {
    It b;
    It e;
    It begin() const { return b; }
    It end() const { return e; }
};

template<class T>
struct value_t {
    T t;
    void operator++(){ t++; }
    auto operator*() { return value_of(*t); }
    friend bool operator==(value_t const& left, value_t const& right)
    { return left.t == right.t; }
    friend bool operator!=(value_t const& left, value_t const& right)
    { return left.t != right.t; }
};

template<class T>
range_t<value_t<T>> values_over(T b, T e) {
    return {{b}, {e}};
}

template<class C>
auto values_of(C& c) {
    using std::begin; using std::end;
    return values_over(begin(c), end(c));
}

int main() {
    X x;
    double i;
    for (double const& it : values_of(x)) {
        i = it;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接