2

我正在尝试在 std::unordered_set 上使用 boost::adaptors::transformed 但即使在很小的实验中似乎也会产生奇怪的行为。

我在 Ubuntu 16.04 上使用 Boost 1.58.0 和 gcc 5.4.0。

在迭代范围时,不会列出范围初始化后添加的元素:

#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;
}

遵循与其他 std 容器(如 std::list)相同的方案按预期工作,将后者添加的元素加倍。

如果使用以下方法初始化集合,则更奇怪的是:

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

范围迭代仅给出 '10' 而容器的 '10 0 1 2 3 4 5'

有人能告诉我这个例子有什么问题吗?

4

2 回答 2

1

transformed不将范围的引用存储为范围;它在范围适配器构建begin时获取范围的/end迭代器。

当您稍后在构建适配器后插入到集合中时,新元素可能不在begin/end插入之前由旧迭代器分隔的范围内。或者,更糟糕的是,如果插入触发了重新散列,则插入将使所有迭代器无效。

于 2017-09-20T22:15:11.703 回答
0

我真的看不出问题:

您的确切示例代码(实时)打印:

8 20 6 4 0 2 
4 10 3 2 0 1 

这似乎是你应该期待的。


使用其他元素

10 8 6 4 2 0 
5 4 3 2 1 0 

主意:

分配模板表达式可能是未定义的行为:

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

因为double_int()可能通过引用保存在适配器transformed中(尚未检查)。

看看这是否为您解决了问题:

for (auto i : set | boost::adaptors::transformed(double_int()))
    std::cout << i << " ";
于 2017-09-20T20:32:44.650 回答