繁体   English   中英

基于范围的for循环用于私有映射值

[英]range-based for loop for private map values

我有以下代码:

#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>

class MyObject
{
public:
    MyObject()
        : m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
    {}

    RETURNTYPE GetStringIterator() const
    {
        IMPLEMENTATION
    }

private:
    std::map<int, std::string> m_Items;
};


int main()
{
    MyObject o;
    for (auto& s : o.GetStringIterator())
    {
        std::cout << s;
    }
}

RETURNTYPEIMPLEMENTATION应该允许MyObject任何客户端(在本例中为main()函数)迭代m_Items映射的值,而不复制任何数据? 似乎这应该可以使用基于c ++ 11范围的循环和迭代器。 但我无法弄清楚如何。

基于范围的迭代可以像这样实现:

class MyObject
{
public:
    MyObject()
        : m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
    {}

    auto begin()       { return m_Items.begin(); }
    auto begin() const { return m_Items.begin(); }
    auto end()       { return m_Items.end(); }
    auto end() const { return m_Items.end(); }

private:
    std::map<int, std::string> m_Items;
};

复制或不复制值取决于代码在调用站点的写入方式:

MyObject a;
for(auto [key,value] : a) {} // copies are made
for(auto & [key,value] : a) {} // no copy
for(auto const & [key,value] : a) {} // no copy

您可以通过删除beginend的非const版本来禁用地图值的修改:

class MyObject
{
public:
    MyObject()
        : m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
    {}

    auto begin() const { return m_Items.begin(); }
    auto end() const { return m_Items.end(); }

private:
    std::map<int, std::string> m_Items;
};

然后,尝试修改范围for for循环中的值将导致编译错误:

MyObject a;
for(auto & [key,value] : a) {
    //value.push_back('a');      // Not OK 
}
for(auto & [key,value] : a) {
    cout << value;             // OK
}

请注意,如果地图是实现细节,则应使用@Barry提出的答案,因为它仅迭代地图的值,而不是关键字。

你可以使用boost::adaptors::map_values ,它适用于C ++ 11:

auto GetStringIterator() const
    // NB: have the move the declaration of m_Items ahead of this function for this to work
    -> decltype(m_Items | boost::adaptors::map_values)
{
    return m_Items | boost::adaptors::map_values;
}

或者它的range-v3等价, view::values 两者都可以像values(m)而不是m | values m | values ,如果你喜欢那样的话。

两种解决方案都会将视图返回到地图的值。 这是一个不拥有任何底层元素的对象,复制起来很便宜 - 即O(1)。 我们不是在对地图或其任何底层元素进行编码。

您可以使用它,就好像它是任何其他范围:

for (std::string const& s : o.GetStringIterator()) {
    // ...
}

此循环不会复制任何字符串。 每个s直接指向map存储的相应string

我将在首先回答这个问题。

这是一个最小的映射iteratoroid:

template<class F, class It>
struct iterator_mapped {
  decltype(auto) operator*() const {
    return f(*it);
  }

  iterator_mapped( F f_in, It it_in ):
    f(std::move(f_in)),
    it(std::move(it_in))
  {}

  iterator_mapped( iterator_mapped const& ) = default;
  iterator_mapped( iterator_mapped && ) = default;
  iterator_mapped& operator=( iterator_mapped const& ) = default;
  iterator_mapped& operator=( iterator_mapped && ) = default;

  iterator_mapped& operator++() {
    ++it;
    return *this;
  }
  iterator_mapped operator++(int) {
    auto copy = *this;
    ++*this;
    return copy;
  }
  friend bool operator==( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
    return lhs.it == rhs.it;
  }
  friend bool operator!=( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
    return !(lhs==rhs);
  }
private:
  F f;
  It it;
};

它在技术上不是一个迭代器,但它有资格for(:)循环。

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
};
template<class It>
range_t<It> range( It b, It e ) {
  return {std::move(b), std::move(e)};
}

以上是一个绝对最小的迭代器范围类型,可以for(:)迭代。

template<class F, class R>
auto map_range( F&& f, R& r ) {
  using std::begin; using std::end;
  auto b = begin(r);
  auto e = end(r);
  using it = iterator_mapped<std::decay_t<F>, decltype(b)>;
  return range( it( f, b ), it( f, e ) );
}

请注意R& not R&& ; 在这里采取r值是危险的。

auto GetStringIterator() const
{
  return map_range( [](auto&& pair)->decltype(auto){
    return pair.second;
  }, m_Items );
}

并做了。

将其转换为是一件痛苦的事。 你必须用std::function s来代替lambdas(或编写执行任务而不是lambda的函数对象),用auto和尾随返回类型替换decltype(auto) ,给lambdas提供auto&&参数的确切类型等等,你最终会得到大约25%-50%的代码,其中大部分都是模糊的类型追逐。

这基本上是boost::adaptors::map_values作用,但是这是手动滚动的,这样你就可以理解它是如何工作的,并且没有boost依赖。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM