c++c++11boostboost-range

How to use boost::range::adaptors::transformed over std::unordered_set?


I'm trying to use boost::adaptors::transformed over a std::unordered_set but seems to produce weird behaviors even on quite small experiments.

I'm using Boost 1.58.0 on Ubuntu 16.04 with gcc 5.4.0.

Added elements after range initialization are not listed while iterating the range:

#include <iostream>
#include <vector>
#include <unordered_set>
#include <boost/range/adaptor/transformed.hpp>

struct double_int
{
    typedef int result_type;
    int operator()(int x) const { return x * 2; }
};

int main()
{
  std::unordered_set<int> set;

  for(int i = 0; i < 5; ++i)
    set.insert(i); //adding ints to set

  auto range = set | boost::adaptors::transformed(double_int());

  set.insert(10); //adding some other int

  //this produces: '8 0 2 4 6'
  for(auto i : range)
    std::cout << i << std::endl;

  //this produces: '10 4 0 1 2 3'
  for(auto i : set)
    std::cout << i << std::endl;

  //element 10 is not doubled!

  return 0;
}

Following the same scheme with other std containers (like std::list) work as intended, doubling the latter added elements.

Even more weirdly if the set is initialized using:

std::unordered_set<int> set = {0,1,2,3,4,5};

range iteration gives only '10' while container's '10 0 1 2 3 4 5'

Could someone tell me what is wrong with this example?


Solution

  • transformed doesn't store a reference to the range as a range; it grabs the begin/end iterators of the range at the time of the range adaptor's construction.

    When you later insert into the set after the adaptor's been constructed, the new element might not be in the range delimited by the old begin/end iterators prior to the insertion. Or, even worse, the insertion will invalidate all the iterators if it triggers a rehash.