【问题标题】:Why does std::set seem to force the use of a const_iterator?为什么 std::set 似乎强制使用 const_iterator?
【发布时间】:2016-12-10 15:23:31
【问题描述】:

考虑下面的简单程序,它尝试使用对其中元素的 NON-const 引用来迭代集合的值:

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

编译时,我从 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) {  
              ^  

是的,我知道我实际上并没有尝试修改 for 循环中的元素。但关键是我应该能够获得一个非常量引用以在循环内使用,因为集合本身不是 const 限定的。如果我创建一个 setter 函数并在循环中使用它,我会得到同样的错误。

【问题讨论】:

  • 如果你真的想修改一个std::set的元素,在原地,你可以使用const_cast。只需真正确定修改不会改变集合中元素的顺序,否则您将遇到未定义的行为。这是非常不安全的,这就是为什么你必须方式不顾一切地去做。

标签: c++ c++11 stdset


【解决方案1】:

来自cpp reference

在集合中,一个元素的值也可以识别它(值是 本身是 T 类型的键),并且每个值必须是唯一的。价值 集合中的元素不能在容器中修改一次( 元素总是 const),但它们可以被插入或删除 容器。

【讨论】:

    【解决方案2】:

    std::set 使用包含的值来形成快速数据结构(通常是红黑树)。改变一个值意味着需要改变整个结构。因此,强制 constness、std::set 可以防止您将其推入不可用状态。

    【讨论】:

    • “不可用”,如“尝试使用时随机崩溃”。
    • @Yakk 更有可能只是无法在某些操作中找到元素,这可以说更糟。
    【解决方案3】:

    行为是设计使然。

    给你一个非常量迭代器可以激发你改变集合中的元素;随后的迭代行为将是未定义的。

    请注意,C++ 标准规定 set&lt;T&gt;::iteratorconst,所以老式的 C++11 之前的方式仍然行不通。

    【讨论】:

      【解决方案4】:

      集合就像没有值的地图,只有键。由于这些键用于加速集合上的操作的树,因此它们无法更改。因此,所有元素都必须是 const 以防止底层树的约束被破坏。

      【讨论】:

      • 我明白了,但我可能正在修改 set 元素中的某些内容,而不会修改其在集合中的排序顺序; http://www.cplusplus.com/reference/set/set/begin 声称非常量集合将从 begin() 中返回一个非常量迭代器
      • std::set 无法知道这一点,因此它使 const 的所有内容都安全。如果您的结构具有可变数据和一些不可变的键字段,那么也许您应该使用以结构为值的映射,以及不可变键字段的副本作为键。如果你知道你没有改变值的顺序,你总是可以 const_cast 去掉 const,但我认为 map 方法更干净。
      • 根据en.cppreference.com/w/cpp/container/setbegin 确实返回iterator 迭代器是const_iterator 自C++11 以来的别名
      • 我相信别名是为了实现通用容器接口,其中所有容器都有iteratorconst_iterator。这可能会与不了解 std::set 详细信息的模板一起使用
      • 这是模板代码在任何地方使用auto 的一个很好的理由,或者至少对它不打算修改的引用进行 const 限定。如果提问者在当前有Int &amp; 的地方用Container::value_type &amp; 编写了一些模板代码,那么模板就不必要地无法为集合实例化。
      猜你喜欢
      • 1970-01-01
      • 2017-05-06
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2017-05-03
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多