boost::any_range and operator []

228 views Asked by At

Consider the following code:

#include <boost/range.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <list>

struct TestData {
  TestData() : m_strMem01("test"), m_intMem02(42), m_boolMem03(true) {}
  std::string m_strMem01;
  int m_intMem02;
  bool m_boolMem03;
};

struct IntComp {
  bool operator()(const TestData &s, int i) { return s.m_intMem02 < i; }
  bool operator()(int i, const TestData &s) { return i < s.m_intMem02; }
  bool operator()(const TestData &i, const TestData &s) {
    return i.m_intMem02 < s.m_intMem02;
  }
};
struct StrComp {
  bool operator()(const TestData &s, const std::string &str) {
    return s.m_strMem01 < str;
  }
  bool operator()(const std::string &str, const TestData &s) {
    return str < s.m_strMem01;
  }
  bool operator()(const TestData &i, const TestData &s) {
    return i.m_strMem01 < s.m_strMem01;
  }
};

typedef boost::any_range<TestData, boost::forward_traversal_tag,
                         const TestData &, std::ptrdiff_t> TestRange;

std::vector<TestData> vecData(10);
std::list<TestData> listData(20);

TestRange foo() {
  TestRange retVal;

  auto tmp1 = std::equal_range(vecData.cbegin(), vecData.cend(), 42, IntComp());
  retVal = boost::join(retVal, tmp1);
  auto tmp2 =
      std::equal_range(listData.cbegin(), listData.cend(), "test", StrComp());
  retVal = boost::join(retVal, tmp2);
  return retVal;
}

int main(int argc, char *argv[]) {
  auto res = foo();
  for (auto a : res) {
    std::cout << a.m_strMem01 << std::endl;
  }
  //std::cout << res[4].m_intMem02 << std::endl;
}

If you uncomment the last line the code fails since distance_to not implemented for any_forward_iterator_interface. I'm not sure what exactly I'm missing here, like implementing operator[] or distance_to but for what? My own version traversal tag? And why it doesn't work in the first place?

Coliru version

2

There are 2 answers

3
Oncaphillis On BEST ANSWER

I would say the answer depends on your performance needs and your laziness when it comes to implementing a new iterator abstraction. The core reason for your [] operator not working is the fact that std::list<...> does not provide a random access traversal iterator. If you would have chosen a container that provides such an iterator. You any_range<...> could have taken the random_access_traversal_tag and everything would be fine. I think it's fair to say that it is not such a big deal to implement a random access iterator on top of a list by simply encapsulating the current index and count forward and backward within the list whenever a specific position is meant to be accessed, but it's clearly against the nature of the list performance-wise.

  • Is there a good reason to hold one of the collection in a list ?
  • Is there a good reason to access the resulting any_range by random ?
  • Is it worth the effort to provide a inefficient random access interface for std::list ?
1
sehe On

Of course any_iterator (which underlies the any_range implementation) doesn't gratuitously emulate RandomAccess iterators for any odd iterator you pass.

If you want that, just make an iterator adaptor that does this (making it very slow to random access elements in a list - so don't do this).