为何此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()
问题根源解析
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是未定义行为的随机表现。
为什么std::move没触发移动构造?
std::move的作用只是将左值转换为右值引用,它本身不会触发任何构造函数。只有用这个右值引用去初始化新对象时,才会调用移动构造函数。- Bad分支只是用右值引用绑定了临时对象的成员,没有创建新的
bar_t实例,自然不会触发移动构造。
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
相关产品推荐
相关产品推荐

