Opened 7 years ago

Last modified 7 years ago

#12071 new Bugs

Using iterator_facade with range-v3 fails to compile

Reported by: Krzysztof Czaiński <1czajnik@…> Owned by: jeffrey.hellrung
Milestone: To Be Determined Component: iterator
Version: Boost 1.61.0 Severity: Problem
Keywords: range-v3 Cc:

Description

The fact that postfix_increment_proxy is not DefaultConstructible and doesn't have a nested public value_type makes iterators unusable with range-v3, which check the concepts.

Example:

#include "boost/iterator/iterator_facade.hpp"
#include "range/v3/utility/iterator.hpp"

template <class V, class Category>
class TestIter
    : public boost::iterator_facade<TestIter<V, Category>, V, Category, V> {
 public:
  using typename boost::iterator_facade<TestIter<V, Category>, V, Category,
                                        V>::difference_type;

  TestIter() : v_() {}

  explicit TestIter(V v) : v_(v) {}

 private:
  friend class boost::iterator_core_access;

  V dereference() const { return v_; }
  bool equal(const TestIter& other) const { return v_ == other.v_; }
  void increment() { ++v_; }
  void decrement() { --v_; }
  void advance(difference_type n) { v_ += n; }
  difference_type distance_to(const TestIter& other) const {
    return other.v_ - v_;
  }

  V v_;
};

using InIter = TestIter<int, std::input_iterator_tag>;

static_assert(ranges::InputIterator<InIter>(), "");

void f(InIter x) {
  static_assert(ranges::Readable<decltype(x++)>(), "");
  static_assert(ranges::DefaultConstructible<decltype(x++)>(), "");
}

I think static_assert(ranges::InputIterator<InIter>(), "") fails, because the two static_asserts in f() fail.

From what I've learned here: https://github.com/ericniebler/range-v3/issues/304 it seems a good idea to fix postfix_increment_proxy to conform to these requirements. Ditto writable_postfix_increment_proxy.

Change History (2)

comment:1 by Krzysztof Czaiński <1czajnik@…>, 7 years ago

Here's a simple fix, which works as long as the value type is DefaultConstructible:

--- boost/boost/iterator/iterator_facade.hpp
+++ boost/boost/iterator/iterator_facade.hpp
@@ -152,8 +152,9 @@
     template <class Iterator>
     class postfix_increment_proxy
     {
+     public:
         typedef typename iterator_value<Iterator>::type value_type;
-     public:
+        postfix_increment_proxy() {}
         explicit postfix_increment_proxy(Iterator const& x)
           : stored_value(*x)
         {}
@@ -178,8 +179,9 @@
     template <class Iterator>
     class writable_postfix_increment_proxy
     {
+     public:
         typedef typename iterator_value<Iterator>::type value_type;
-     public:
+        writable_postfix_increment_proxy() {}
         explicit writable_postfix_increment_proxy(Iterator const& x)
           : stored_value(*x)
           , stored_iterator(x)

To work around values which aren't DefaultConstructible, it might be necessary to wrap the stored_value in optional, but the above simple fix will work for majority of cases.

comment:2 by Krzysztof Czaiński <1czajnik@…>, 7 years ago

Summary: postfix_increment_proxy is not DefaultConstructible and doesn't have a nested public value_typeUsing iterator_facade with range-v3 fails to compile

Maybe a high-level example will be helpful:

#include <iostream>

#include "third_party/boost/allowed/iterator/iterator_facade.hpp"
#include "third_party/range_v3/include/range/v3/algorithm/copy.hpp"
#include "third_party/range_v3/include/range/v3/iterator_range.hpp"
#include "third_party/range_v3/include/range/v3/utility/iterator.hpp"

template <class V, class Category>
class TestIter
    : public boost::iterator_facade<TestIter<V, Category>, V, Category, V> {
 public:
  using typename boost::iterator_facade<TestIter<V, Category>, V, Category,
                                        V>::difference_type;

  TestIter() : v_() {}

  explicit TestIter(V v) : v_(v) {}

 private:
  friend class boost::iterator_core_access;

  V dereference() const { return v_; }
  bool equal(const TestIter& other) const { return v_ == other.v_; }
  void increment() { ++v_; }
  void decrement() { --v_; }
  void advance(difference_type n) { v_ += n; }
  difference_type distance_to(const TestIter& other) const {
    return other.v_ - v_;
  }

  V v_;
};

using InIter = TestIter<int, std::input_iterator_tag>;

template <class R>
void print(R&& r) {
  ranges::copy(std::forward<R>(r), ranges::ostream_iterator<>(std::cout, ", "));
  std::cout << "\n";
}

int main() {
  print(ranges::make_iterator_range(InIter(1), InIter(10)));
}
Note: See TracTickets for help on using tickets.