【问题标题】:How to declare boost range adaptor (e.g. map_values)如何声明提升范围适配器(例如 map_values)
【发布时间】:2014-02-19 21:48:08
【问题描述】:

说我有

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 方法,它基本上可以输出 我的地图 | boost::adaptors::map_values 并允许在 MyClass 方法之外方便地进行值迭代。如何声明这样的方法?我需要实现某种伪容器和相应的迭代器还是有捷径?

【问题讨论】:

    标签: c++ boost c++11 map boost-range


    【解决方案1】:

    基本上,您有两个不错的选择。您可以使用 boost 的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; }
    }; 
    

    【讨论】:

    • 不幸的是,这不会在 VS2013 中编译
    • 对不起,我没有意识到你使用的是 VS2013。一般来说,如果您想在 VS2013 中使用答案,您应该在问题中提及这一点,因为 Microsoft 仍然不支持 C++11 标准。我的代码可以在任何 C++11 编译器上运行,但我只是将其编辑为在 Visual Studio 中也能运行。
    • @rmcclelan 感谢您的帮助。我仍然无法让它工作。我在 my_map 声明之后将你的 typedef 和 Values() 方法放在类声明中,它在 VS2013 中无法编译。
    • LRaiz,你能提供一个错误信息吗?谢谢。
    • 首先是我的代码(所以仍然无法掌握markdown)#include #include #include 类Foo { std::map my_map; typedef boost::iterator_range values_range; values_range Values() { return values_range(my_map | boost::adaptors::map_values); } };
    【解决方案2】:

    使用伪容器或迭代器适配器并非绝对必要,例如 boost::iterator_range。尽管使用适当的适配器(例如 iterator_range)理论上会更正确和通用,并且不会违反应用于对象的principle of least knowledge,但您可能希望避免它,因为有额外的间接性,或者可能因为您的迭代器通常只是一个单程范围,而不是前进范围。

    所以如果你想直接使用适配的范围,你可以简单地使用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
    */
    

    如果你想让暴露的值成为一个 const 成员函数,那就稍微复杂一点:

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

    【讨论】:

    • 不幸的是,下面的代码不能用 VS2013 编译。 #include #include class Bar { std::map _myMap; decltype(_myMap | boost::adaptors::map_values) values() { return _myMap | boost::adaptors::map_values; } }
    • 错误 1 ​​错误 C2678: 二进制 '|' : 未找到采用“未知”类型左操作数的运算符(或没有可接受的转换) 错误 2 错误 C2440: 'return' : 无法从 'boost::range_detail::select_second_mutable_range<:map>,std::allocator<:pair _kty>>>>' 到 'int'
    • @LRaiz 1. 当然,这段代码是有效的 C++11,它可以编译和工作,see it live on coliru,2. 你为问题指定的标签不包括 VS,所以我不'不明白你为什么不接受它。最好在一个单独的问题中调查视觉工作室的局限性。 (并且您需要 11 月 CTP 版本才能获得 VS 的 C++11 功能的最佳子集。)
    猜你喜欢
    • 2020-12-10
    • 1970-01-01
    • 2013-05-14
    • 2017-11-14
    • 2017-10-30
    • 2013-01-24
    • 2014-12-10
    • 2017-11-30
    • 1970-01-01
    相关资源
    最近更新 更多