1

我正在玩 Move Semantics 和 [r|l]value 引用来学习如何在实际程序中使用它们。考虑以下代码:

// Item is a heavy class having move ctor and assignment but no copy. 
std::map<std::string, Item*> lookup;
std::forward_list<Item> items;
void FooClass::addItem(Item&& d) {
    if (lookup.find(d.getName()) == lookup.end()) {
        lookup[d.getName()] = &d;    //<== not safe after move?
        items.push_front(std::move(d));
    }
}

我正在获取地址Item&&并将其存储在指针中。然后将数据移动到std::forward_list( items)。我假设调用移动赋值不会影响对象的地址。那是对的吗?虽然d移动后的内容不再有效。即查找表 ( lookup) 的内容不正确。

我假设我必须重新排序 a) 添加查找项和 b) 移动实际数据。上面的代码不理智。这个对吗?

我也不明白为什么我必须在std::move那里说。编译器应该知道这d是一个右值引用。所以它应该调用std::forward_list<T>::push_front(T&&)并移动分配......

4

2 回答 2

6
    lookup.[d.getName()] = &d;    //<== not safe after move?

这是完全不安全的,但不仅仅是因为搬家。与您的问题标题相反,您不是在获取右值引用的地址,而是在获取左值的地址,而是在函数返回后不久可能会超出范围的地址,这将留下一个悬空指针。考虑:

FooClass f;
f.addItem( Item() );

这会将临时地址添加到映射中,因此如果您取消引用指针,您的程序具有未定义的行为,这就是不安全的缩影。

下一行的移动可能会使事情变得更糟,因为映射中指针所引用的对象可能会被移动修改,从而在映射中留下一个指向移动源的指针Item,但这与未定义的行为相比毫无意义函数返回后它超出范围的结果。

使代码安全是微不足道的,因此没有理由按照您的方式编写它。

    items.push_front(std::move(d));
    auto& item = items.front();
    lookup[item.getName()] = &item;

现在映射中的指针指向一个不会超出范围的对象。只要元素在forward_list.

于 2013-08-07T20:50:53.467 回答
1

我会摆脱items并更改为按值lookup存储,如下所示:Item

using Lookup = std::map<std::string, Item>;
Lookup lookup;

void addItem(Item&& d)
{ lookup.insert(std::pair<std::string const&, Item&&>{d.getName(), std::move(d)}); }
于 2013-08-07T21:42:59.910 回答