Search code examples
c++booststlboost-range

boost::any_range and operator []


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


Solution

  • 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 ?