【问题标题】:Creating a custom range class that wraps boost adaptors创建一个包含 boost 适配器的自定义范围类
【发布时间】:2018-10-17 21:41:51
【问题描述】:

我有一个类可以将一些 boost 变换适配器应用于一个范围(例如,实际上它比这复杂得多):

struct Foo {
    auto range() const {
        return boost::irange(0, 10)
            | boost::adaptors::transformed([] (auto x) { return x * 2; });
    }

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

仅此一项就允许我们使用以下范围迭代 Foo

for (auto x : Foo()) {
    std::cout << num << std::endl;
}

但是,这与其他 boost 适配器或范围操作(如 boost::join)不能很好地组合:

auto bad = boost::join(boost::irange(0, 10), Foo());
auto also_bad = Foo() | boost::adaptors::transformed([] (auto x) { return x + 1; });

上述两种情况都会引发一些令人讨厌的模板错误。前者(bad):

In file included from test.cpp:4:
/usr/local/include/boost/range/join.hpp:30:70: error: no type named 'type' in
      'boost::range_iterator<const Foo, void>'
            BOOST_DEDUCED_TYPENAME range_iterator<SinglePassRange1>::type,
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/join.hpp:44:28: note: in instantiation of template class
      'boost::range_detail::joined_type<const Foo, const boost::integer_range<int> >' requested
      here
    : public range_detail::joined_type<SinglePassRange1, SinglePassRange2>::type
                           ^
test.cpp:34:16: note: in instantiation of template class 'boost::range::joined_range<const Foo,
      const boost::integer_range<int> >' requested here
    auto bad = boost::join(Foo(), range);
               ^

...

而后者(also_bad):

In file included from test.cpp:1:
In file included from /usr/local/include/boost/range/any_range.hpp:17:
In file included from /usr/local/include/boost/range/detail/any_iterator.hpp:22:
In file included from /usr/local/include/boost/range/detail/any_iterator_wrapper.hpp:16:
In file included from /usr/local/include/boost/range/concepts.hpp:24:
/usr/local/include/boost/range/value_type.hpp:26:70: error: no type named 'type' in
      'boost::range_iterator<Foo, void>'
    struct range_value : iterator_value< typename range_iterator<T>::type >
                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/include/boost/range/adaptor/replaced.hpp:109:40: note: in instantiation of template
      class 'boost::range_value<Foo>' requested here
                BOOST_DEDUCED_TYPENAME range_value<SinglePassRange>::type>& f )
                                       ^
test.cpp:35:27: note: while substituting deduced template arguments into function template
      'operator|' [with SinglePassRange = Foo]
    auto also_bad = Foo() | boost::adaptors::transformed([] (auto x) { return x * 2; });
                          ^
...

这两个错误似乎都在抱怨Foo 不是一个范围。我尝试按照建议的hereiterator/const_iterator 添加operator OutContainer() 和typedef,但无济于事。我必须对Foo 做些什么才能让它与这些范围操作很好地配合?

【问题讨论】:

标签: c++ boost c++17 c++20


【解决方案1】:

这个错误在这种情况下非常有用。您的类型必须建模 SinglePassRange 而它没有。 page 关于如何执行此操作,虽然您提供了 begin()end(),但您没有提供 iteratorconst_iterator 的类型别名。因此,您不会为 SinglePassRange 建模。

但你的代码失败实际上是一件好事,因为它也很糟糕。你有begin() 呼叫range().begin()end() 呼叫range().end()。这些是进入不同范围的迭代器。所以无论如何这都是未定义的行为 - 你没有满足 SinglePassRange 的语义概念。

更简单的解决方案是直接使用Foo()。这已经有效:

auto good = boost::join(boost::irange(0, 10), Foo().range());
auto also_good = Foo().range() | boost::adaptors::transformed([] (auto x) { return x + 1; });

这意味着您只需编写一个函数:range()

【讨论】:

  • 谢谢!我知道这个问题听起来好像我懒惰地没有阅读任何文档。我已阅读该页面,但不明白其中与我的问题相关的内容。提供range() 方法要简单得多。虽然,如果我选择了使Foo range-able 的路线(如果这是正确的术语)。从begin()end() 返回我需要什么?为什么 range().begin()range().end() 迭代器进入不同的范围? auto r = range() 然后 r.begin()r.end() 会更正确吗?
  • 进一步(抱歉堆积),如果是这种情况,为什么范围不像迭代器(对 begin()end() 的调用总是兼容的)?尽管SinglePassRange 仅针对SinglePassIterator 定义(其中的文档仅提供opsem),但我仍不清楚以下内容:它只是命名不佳,还是我只能迭代一个类型建模一次?似乎 begin() 迭代器是可复制构造的,可以让我保留它(然后总是使用一个新的开始来开始迭代)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-08
  • 2012-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-02
相关资源
最近更新 更多