【问题标题】:Vector of non-const objects seems to be treated as constant in range-based for loop非常量对象的向量似乎在基于范围的 for 循环中被视为常量
【发布时间】:2017-12-27 16:54:57
【问题描述】:

我有一个 std::vector 的对象通过在 push_back 调用中取消引用 std::unique_ptr 来填充。但是,当我运行基于可变范围的 for 循环时,我对这些对象的修改保留在循环的本地。换句话说,尽管循环中缺少const 关键字,但这些对象似乎被视为常量。这是演示我所看到内容的最少代码:

#include <vector>
#include <memory>
#include <iostream>

class Item
{
    public:
        typedef std::unique_ptr<Item> unique_ptr;

        inline static Item::unique_ptr createItem()
        {
            return std::unique_ptr<Item>(new Item());
        }

        inline const int getValue() const { return _value; }
        inline void setValue(const int val) { _value = val; }

    private:
        int _value;
};

int main()
{
    std::vector<Item> _my_vec;
    for (int i = 0; i < 5; i++)
    {
        Item::unique_ptr item = Item::createItem();
        _my_vec.push_back(*item);
    }

    for (auto item : _my_vec)
    {
        // modify item (default value was 0)
        item.setValue(10);

        // Correctly prints 10
        std::cout << item.getValue() << std::endl;
    }


    for (auto item : _my_vec)
    {
        // Incorrectly prints 0's (default value)
        std::cout << item.getValue() << std::endl;
    }

}

我怀疑这与std::unique_ptr 的移动语义有关?但这不太有意义,因为即使 push_back 正在调用复制构造函数或其他东西并复制添加的项而不是指向它,迭代器仍然会传递相同的副本,不是吗?

有趣的是,在我的实际代码中,这里由Item 表示的类有一个成员变量,它是指向另一个类的对象的共享指针vector,以及对这些共享指针指向的对象的修改在循环之间持续存在。这就是为什么我怀疑unique_ptr 有什么奇怪的地方。

谁能解释这种行为并解释我如何在仍然使用指针的同时解决这个问题?

【问题讨论】:

  • 使用for (auto&amp; item : _my_vec)。没有&amp;,它按值访问
  • @pergy:哦,伙计.... duh。你说得对。我花了很多时间迭代我的共享指针容器,我忘记了如何正确地迭代实际对象。谢谢你。只是去展示它通常是如何显而易见的。马,不是斑马,等等……
  • 很好地提出了问题:)
  • @marcman 如果您尝试修改的内容被视为常量,它将无法编译。
  • @LogicStuff:这就是我如此困惑的原因。当然比那简单多了……

标签: c++ c++11 pointers smart-pointers unique-ptr


【解决方案1】:

当你像这样编写基于范围的 for 循环时:

std::vector<int> v = ...;
for(auto elt : v) {
   ...
}

v 的元素被复制到elt

在您的示例中,在每次迭代中,您都修改了 Item 的本地副本,而不是向量中的 Item

要解决您的问题,请使用参考:

for (auto& item : _my_vec)
{
    item.setValue(10);
    std::cout << item.getValue() << std::endl;
}

【讨论】:

    【解决方案2】:

    非常量对象的向量似乎被视为常量

    如果它被当作常量处理,那么编译器就会对你大喊大叫,因为写入常量会被视为格式错误,编译器会被要求对你大喊大叫。显示的代码编译得很好,没有警告。

    我怀疑您可能指的是您没有修改向量中的元素这一事实。那是因为你修改了auto item。该项目不是向量的元素,它是向量中项目的副本。您可以通过使用引用来引用该向量中的项目:auto&amp; item。那么对item的修改将是对向量的引用元素的修改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-04
      • 2020-05-03
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-26
      相关资源
      最近更新 更多