【问题标题】:Copy elision and move semantic in return value复制省略并在返回值中移动语义
【发布时间】:2018-12-15 22:01:36
【问题描述】:

背景。

我曾经写过 C++ 代码,现在我又开始了。我正在阅读一些涵盖 c++11(及更高版本)功能的书籍。堆栈溢出问题也有很多复杂的答案,但我只需要一个简单的答案。

在处理指针时,我曾经遵循著名的三规则,但现在我看到有新的移动语义,因此该规则已重命名为五法则。在线阅读我已经看到 C++ 已经发展,现在还有复制省略。我现在有很多新概念。看看这个:

struct Test {};

auto getNewTest() { return Test(); }
auto x = getNewTest();

在这种情况下(假设启用了复制省略),唯一发生的副本应该是当我将值分配给 x 时(只有 1 个副本)。之前会有 2 个副本:1 个作为返回值,1 个作为 x 的值。

到目前为止一切顺利,但使用移动语义我可以实现相同的效果(如果我是正确的)!假设 Test 已经正确分配了移动操作;我可以这样称呼:

auto getNewTest() { return Test(); }
auto x = std::move(getNewTest());

问题

我明白,当我必须摆脱旧变量时,移动是好的,我可以将其所有内容移动到新变量中,而不是将其复制到新变量中。

碰巧我有一个像return SomeClass(1,2,3) 这样的声明,我想避免复制太多。 std::move() 是否执行零拷贝?因为复制省略避免了 1 个副本(返回值)并且由于复制构造函数而只执行 1 个副本。

我的猜测是,使用 std::move() 我窃取了从函数创建的对象,因此我的副本为零。我对么?总的来说,这可能是平庸的,但我花了很多时间。

【问题讨论】:

  • 您的第一个示例将在任何体面的编译器上执行零拷贝。
  • std::move 实际上什么都不做,它只用于将值传递给采用右值引用的重载函数变体。请注意,在 C++17 中,这些规则已更改(并且可能会在 C++20 中再次更改),因此在您的第一个示例中不会发生复制或移动。
  • std::move(getNewTest()); 没有意义,结果已经是右值了。
  • 我正在使用 Visual Studio,它应该有一个不错的编译器,所以我不会担心。
  • 嗯,vc++ 编译器因忽视标准、按自己的方式做事和忽视性能问题而臭名昭著。最近情况发生了变化,但是 vc++ 不支持 C++11 模式。只能在某些混合C++14+模式或C++17模式下编译。

标签: c++ c++11


【解决方案1】:

你想太多了。使用您编写的代码,

auto x = getNewTest();

实际上是在适当的位置构造x,这几乎是您可以获得的效率。

这种行为在 C++17 中是有保证的,但实际上它已经以这种方式工作了很长时间。

更多here。我不能把 cppreference 推荐得太高,虽然它不是权威的。

【讨论】:

    猜你喜欢
    • 2021-02-08
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 2015-07-14
    • 2020-10-01
    相关资源
    最近更新 更多