首页 > 解决方案 > 在纸牌游戏中移动语义

问题描述

我正在写一个纸牌后端C++。如果我不必怀疑某些东西是否被复制或移动,生活会更轻松,但C++在这种情况下有什么意义呢?我只会使用 Python :D。

Anyhoo - 我有一个遵守规则 5 的 Card 对象(它有一个构造函数、复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符)。我为各种卡片组设置了单独的课程。

到目前为止,我一直在使用 astd::vector<Card> cards来存储卡片,每次我需要将卡片从一个堆栈移动到另一个堆栈时,我都会这样做:

auto last_card = cards.back(); // Get the last element in the collection.
cards.pop_back(); // Remove the last element.
return std::move(last_card); // Move the card and return it.

我的问题是 - 这是正确的方法吗?我是否应该改为使用一个std::vector<std::unique_ptr>,以便我所做的只是复制/移动指针而不是真实对象?

我正在寻找最佳实践,因为我还是 C++ 新手。任何提示/建议将不胜感激。

谢谢!

编辑:澄清一下,Card对象真的很简单。只是一套西装,价值和颜色(由西装推断)。所有 3 个都是枚举,并且还有额外的辅助函数用于翻转卡片、检查卡片是正面朝上还是朝下等。

标签: c++

解决方案


这非常好,除了几个细节:

return std::move(last_card); // Move the card and return it.

您需要或应该这样做是一个常见的误解。在一个return语句中,右边的东西自动是一个xvalue,因此它已经处于可移动状态。从这个意义上说,std::move那里是多余的。

但它比这更糟一点,因为通过编写std::move,您使编译器更难完全忽略甚至移动 - 通过编写return last_card,您将首先获得您想要的移动,甚至更好的总返回值省略,避免整个shebang。

现在,目前您有一份来自cards.back(); 该cards.back()表达式在那里不是右值,因为它是对其他地方存在的东西的引用,这基本上是一个左值!所以你可能想做:

auto last_card = std::move(cards.back());

另外,我只是想提醒您注意“移动卡片并返回它”的观察:记住,std::move不会移动任何东西,它只是给您一个指代事物的右值表达式。如果发生移动,它将返回操作中,而不是在它之前。

总之,我的建议:

// Extract the last element in the collection
auto last_card = std::move(cards.back());

// Remove the now-dead last element
cards.pop_back();

// Return the new card (will automatically be moved if elision doesn't occur)
return last_card;

但是,根据您对 的描述,这一切都没有实际意义Card,这非常简单,并且不会从移动中获得任何好处。坦率地说,您可以复制它并获得相同的结果。尽管如此,为它在未来变得更加复杂做好准备是件好事。当然,这里没有什么值得尖叫的unique_ptr


推荐阅读