【问题标题】:C++11 move to local const reference: scope [duplicate]C ++ 11移动到本地常量引用:范围[重复]
【发布时间】:2018-10-03 19:27:58
【问题描述】:

对于常规的本地 const 引用变量,范围是 prolonged。这就是以下代码按预期工作的原因:

#include <iostream>
#include <memory>

struct foo
{
        foo()
        {
                std::cout << "foo() @" << (void*)this << std::endl;
        }
        ~foo()
        {
                std::cout << "~foo() @" << (void*)this << std::endl;
        }
};

int main()
{
        auto const& f = std::make_shared<foo>();
        std::cout << "f = " << f.get() << std::endl;
        return 0;
}

// prints:
// foo() @0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() @0x55f249c58e80

使用std::move() 分配移动的对象时,这似乎无法按预期工作:

#include <iostream>
#include <memory>
#include <list>

struct foo
{
        foo()
        {
                std::cout << "foo() @" << (void*)this << std::endl;
        }
        ~foo()
        {
                std::cout << "~foo() @" << (void*)this << std::endl;
        }
};

int main()
{
        std::list<std::shared_ptr<foo>> l;
        l.push_back(std::make_shared<foo>());
        auto const& f = std::move(l.front());
        l.clear();
        std::cout << "f = " << f.get() << std::endl;
        return 0;
}

// prints
// foo() @0x564edb58fe80
// ~foo() @0x564edb58fe80
// f = 0x564edb58fe80

std::move() 确实改变了范围,还是我在处理编译器错误?

将变量从 auto const&amp; f 更改为 auto f 可以解决问题。如果我将移动包装到另一个函数中,它也可以工作:

auto const& f = [&]() { return std::move(l.front()); }();

这几乎就像std::move() 不共享与函数调用相同的语义,而是好像它只是一个常规的变量赋值:

auto const& f = std::move(l.front());

【问题讨论】:

  • 你误解了std::move,但我不确定到底是怎么回事。 std::move 在您展示的任何示例中都没有效果,除了可能禁用某些优化。 std::move 只是从T&amp;T&amp;&amp; 的转换,它将左值引用转换为右值引用。
  • “使用 std::move() 分配移动对象时”看起来有必要对某些人重复一遍又一遍:std::move() 本身不会移动任何东西
  • @FrançoisAndrieux 它确实会影响我的回答
  • 我意识到它在这个特定的示例中没有任何影响。我实际上是在将一个对象从 std::list 元素移动到一个本地 const 引用变量中,然后从列表中删除该条目,并期望该变量仍然有效。
  • 我用我实际遇到的情况更新了我的问题中的第二个 sn-p。使用std::move() 将元素移出std::list。将 auto const&amp; f 更改为 auto f 可以解决此问题,但同时我也希望出现相同的行为。

标签: c++ scope reference constants move


【解决方案1】:

让我们抛开std::move(),创建这个函数:

struct sometype {};

const sometype &foobar( const sometype &cref ) { return cref; }

现在我们使用它:

const sometype &ref = foobar( sometype() );

您认为,在这种情况下,临时工的寿命会延长吗?不,它不会。只有当您直接分配给参考时,生命周期才会延长。当它通过一个函数或通过static_caststd::move 时,延长就消失了。所以你和std::move()有完全相同的问题

struct sometype {
    sometype() { std::cout << "ctype()" << std::endl; }
    ~sometype() { std::cout << "~ctype()" << std::endl; }
};

const sometype &foobar( const sometype &cref ) { return cref; }

int main()
{
    const sometype &cref1 = foobar( sometype() );
    sometype &&cref2 = std::move( sometype() );
    std::cout << "main continues" << std::endl;
}

输出:

ctype()
~ctype()
ctype()
~ctype()
main continues     

live example

注意:你不应该在 return 语句中使用std::move(),它不会给你任何东西。

为您的代码更改。你应该记住std::move() 不会移动任何东西。它由特殊的赋值运算符或构造函数完成(如果它们提供了一个类型)。所以当你写这段代码时:

const type &ref = std::move( something );

不涉及构造函数或赋值运算符,因此不会发生移动。要进行实际移动,您必须将其分配给一个变量:

type val = std::move( something );

如果可能,现在将进行移动或复制。

【讨论】:

  • 这确实不能回答问题。它只是显示了一个更好的(?)mcve
  • 该注释并非 100% 正确。有时,需要std::move 以避免复制。
  • @Rakete1111 我没有说这是因为复制,复制或生命周期延长是否消失都没有关系,因为临时是函数的参数并且不直接分配给引用。看我的例子
  • 我知道std::move() 在示例中并没有真正做任何事情。我将第二个示例更新为我遇到的情况:将元素从 std::list 移出到本地 const 引用变量中,然后删除该元素,然后使用本地变量(期望它仍然有效)。
  • @Tom 我已经回答了,你的临时值被传递给std::move() 而不是参考,所以寿命延长已经消失了。你的新代码有不同的问题——你有悬空的参考,你不应该从这样的代码中跳出来。这是两种不同的情况。
猜你喜欢
  • 2016-05-31
  • 2012-02-09
  • 2017-08-11
  • 2020-12-07
  • 2014-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多