简体   繁体   English

为什么 std::set 似乎强制使用 const_iterator?

[英]Why does std::set seem to force the use of a const_iterator?

Consider the simple program below, which attempts to iterate through the values of a set using NON-const references to the elements in it:考虑下面的简单程序,它尝试使用对集合中元素的非常量引用来遍历集合的值:

#include <set>
#include <iostream>

class Int
{
public:
   Int(int value) : value_(value) {}
   int value() const { return value_; }
   bool operator<(const Int& other) const { return value_ < other.value(); }
private:
   int value_;
};

int
main(int argc, char** argv) {
   std::set<Int> ints;
   ints.insert(10);
   for (Int& i : ints) {
      std::cout << i.value() << std::endl;
   }
   return 0;
}

When compiling this, I get an error from gcc:编译时,我从 gcc 收到错误:

test.c: In function ‘int main(int, char**)’:
test.c:18:18: error: invalid initialization of reference of type ‘Int&’ from expression of type ‘const Int’  
for (Int& i : ints) {  
              ^  

Yes, I know I'm not actually trying to modify the elements in the for loop.是的,我知道我实际上并没有尝试修改 for 循环中的元素。 But the point is that I should be able to get a non-const reference to use inside the loop, since the set itself is not const qualified.但关键是我应该能够获得在循环内使用的非 const 引用,因为集合本身不是 const 限定的。 I get the same error if I create a setter function and use that in the loop.如果我创建一个 setter function 并在循环中使用它,我会得到同样的错误。

A set is like a map with no values, only keys. 集合就像没有值的地图,只有键。 Since those keys are used for a tree that accelerates operations on the set, they cannot change. 由于这些键用于加速集合操作的树,因此它们无法更改。 Thus all elements must be const to keep the constraints of the underlying tree from being broken. 因此,所有元素必须是const,以防止底层树的约束被破坏。

std::set uses the contained values to form a fast data structure (usually, a red-black tree). std::set使用包含的值来形成快速数据结构(通常是红黑树)。 Changing a value means the whole structure needs to be altered. 更改值意味着需要更改整个结构。 So, forcing const ness, std::set prevents you from pushing it into a non-usable state. 因此,强制conststd::set阻止您将其推入不可用状态。

From the cpp reference : cpp参考

In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. 在集合中,元素的值也标识它(值本身是类型T的键),并且每个值必须是唯一的。 The value of the elements in a set cannot be modified once in the container (the elements are always const ), but they can be inserted or removed from the container. 集合中元素的值不能在容器中修改一次(元素总是const ),但可以在容器中插入或删除它们。

The behaviour is by design. 行为是设计的。

Giving you a non-const iterator could inspire you to change the element in the set; 给你一个非const迭代器可以激发你改变集合中的元素; the subsequent iterating behaviour would then be undefined. 随后的迭代行为将是未定义的。

Note that the C++ standard says that set<T>::iterator is const so the old-fashioned pre C++11 way still wouldn't work. 请注意,C ++标准说set<T>::iteratorconst所以老式的前C ++ 11方法仍然不起作用。

Adding on nate's answer :添加内特的答案

A set is like a map with no values, only keys.集合就像 map,没有值,只有键。 Since those keys are used for a tree that accelerates operations on the set, they cannot change.由于这些键用于加速集合操作的树,因此它们不能更改。 Thus all elements must be const to keep the constraints of the underlying tree from being broken.因此,所有元素都必须是const以防止底层树的约束被破坏。

With C++17 there is the new extract member function, so an alternative to const_cast could be:对于 C++17 有新的extract成员 function,因此const_cast的替代方案可能是:

#include <iostream>
#include <string_view>
#include <set>

struct S
{
  int used_for_sorting;
  bool not_used_for_sorting;

  bool operator<(const S &rhs) const
  { return used_for_sorting < rhs.used_for_sorting; }
};

void print(std::string_view comment, const std::set<S> &data)
{
  std::cout << comment;
  for (auto datum : data)
    std::cout << " {" << datum.used_for_sorting
              << ',' << datum.not_used_for_sorting
              << '}';

  std::cout << '\n';
}

int main()
{
  std::set<S> cont = {{1, false}, {2, true}, {3, false}};

  print("Start:", cont);

  // Extract node handle and change key
  auto nh = cont.extract({1, false});
  nh.value().not_used_for_sorting = true;

  print("After extract and before insert:", cont);

  // Insert node handle back
  cont.insert(std::move(nh));

  print("End:", cont);
}

Probably useful as hot-fix.可能用作热修复。 In general it's hard to see any advantage over a std::map .一般来说,很难看出比std::map有任何优势。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么“std :: begin()”在这种情况下总是返回“const_iterator”? - Why does “std::begin()” always return “const_iterator” in such a case? 我从 std::set 得到 const_iterator 而不是迭代器 - I get const_iterator instead of iterator from std::set 在 std::set 的定义中将 const_iterator 转换为 iterator&amp; - Cast of const_iterator to iterator& in definition of std::set 为什么不 std::set<k, c, a> ::erase 采用 const_iterator?</k,> - Why doesn't std::set<K, C, A>::erase take a const_iterator? 为什么传递给函数set :: iterator而不是const_iterator违反了One Definition Rule? - Why does passing to a function a set::iterator instead of a const_iterator violate the One Definition Rule? 为什么将std :: vector :: erase参数更改为const_iterator? - Why were the std::vector::erase parameters changed to const_iterator? 为什么const_iterator可以与std :: map :: erase一起使用 - Why const_iterator could be used with std::map::erase 为什么std :: map :: const_iterator在std :: for_each期间调用std :: pair构造函数,但是一个简单的for循环却没有? - Why does std::map::const_iterator call the std::pair constructor during a std::for_each, but a simple for loop does not? 有没有办法使用独立的`std :: begin`和const_iterator? - Is there a way to use standalone `std::begin` and for a const_iterator? 为什么斯科特·迈耶斯(Scott Meyers)建议比起const_iterator更喜欢使用“迭代器” - Why does Scott Meyers recommend to prefer `iterator` to `const_iterator`
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM