You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何此std::move场景下移动构造函数未被调用?

为什么调用std::move后移动构造未触发,还返回0?

示例代码

#include <memory> // for class template `unique_ptr`
#define LOG() std::printf("[%p] %s\n", this, __PRETTY_FUNCTION__)

class bar_t final
{
public:
    bar_t(int val) : m_val(val) { LOG(); }
    ~bar_t(void) { LOG(); }
    bar_t(bar_t&& dying) : m_val(std::move(dying.m_val)) { LOG(); }
    int get_value(void) const { return m_val; }
private:
    int m_val;
};

class foo_t final
{
public:
    foo_t(int a_val) : m_bar(a_val) { LOG(); }
    ~foo_t(void) { LOG(); }
    bar_t m_bar;
};

std::unique_ptr<foo_t> gen_foo(int val)
{
    return std::make_unique<foo_t>(val);
}
int main(int argc, char *argv[])
{
#if 1
    bar_t&& bar = std::move(gen_foo(42)->m_bar); // Bad
//  bar_t& bar = gen_foo(42)->m_bar; // gives same result as previous line
#else
    bar_t bar(std::move(gen_foo(42)->m_bar)); // Good
#endif
    std::printf("bar.get_value() = %d\n", bar.get_value());
    return 0;
}

两种分支的输出结果

当#if条件为1(Bad分支)时的输出:

[0x5616d6510e70] bar_t::bar_t(int)
[0x5616d6510e70] foo_t::foo_t(int)
[0x5616d6510e70] foo_t::~foo_t()
[0x5616d6510e70] bar_t::~bar_t()
bar.get_value() = 0

当#if条件为0(Good分支)时的输出:

[0x55acef3bfe70] bar_t::bar_t(int)
[0x55acef3bfe70] foo_t::foo_t(int)
[0x7fff70612574] bar_t::bar_t(bar_t&&)
[0x55acef3bfe70] foo_t::~foo_t()
[0x55acef3bfe70] bar_t::~bar_t()
bar.get_value() = 42
[0x7fff70612574] bar_t::~bar_t()

问题根源解析

  1. Bad分支的核心:悬垂引用

    • gen_foo(42)返回的是临时unique_ptr<foo_t>,这个临时对象在当前语句执行完毕后就会被销毁(没有变量持有它)。
    • std::move(gen_foo(42)->m_bar)只是把foo_t成员m_bar转换成右值引用,但本质还是引用,并没有创建新的bar_t对象。
    • 临时unique_ptr销毁时,会自动释放其指向的foo_t实例,foo_t的析构函数会销毁成员m_bar。此时bar引用的是已经被销毁的对象,属于悬垂引用。后续调用bar.get_value()访问的是已释放的内存,返回0是未定义行为的随机表现。
  2. 为什么std::move没触发移动构造?

    • std::move的作用只是将左值转换为右值引用,它本身不会触发任何构造函数。只有用这个右值引用去初始化新对象时,才会调用移动构造函数。
    • Bad分支只是用右值引用绑定了临时对象的成员,没有创建新的bar_t实例,自然不会触发移动构造。
  3. Good分支为什么正常?

    • bar_t bar(std::move(gen_foo(42)->m_bar))是用右值引用初始化新的bar_t对象,此时会触发bar_t的移动构造函数,把临时foo_t中m_bar的值转移到新的bar对象中。
    • 虽然临时unique_ptr和foo_t对象仍会被销毁,但新的bar对象已经持有了转移过来的值,所以后续访问get_value()能得到正确的42。

内容的提问来源于stack exchange,提问作者Cody

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 05:30:44