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

隐式转换为std::optional是否必然调用移动构造函数?

std::optional返回时移动构造的合法性与调用保证

先看你给出的代码示例:

#include <iostream>
#include <optional>

class Foo {
public:
    Foo() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    ~Foo() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo(const Foo&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo& operator=(const Foo&) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }
    Foo(Foo&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo& operator=(Foo&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }

    int data[100];
};

std::optional<Foo> fn(const Foo& in)
{
    Foo out = in;
    return out; // invokes Foo's move constructor
}

std::optional<Foo> fn2(const Foo& in)
{
    Foo out = in;
    return std::optional<Foo>(out); // invokes Foo's copy constructor
}

int main()
{
    fn(Foo{});
    fn2(Foo{});
    return 0;
}

1. 移动构造的合法性

这种场景下调用移动构造是完全合法的。
原因在于:fn函数中返回的out是函数内的局部自动变量,当直接返回这个变量时,C标准允许编译器将该左值视为亡值(xvalue),从而匹配Foo的移动构造函数。而std::optional<Foo>的构造函数可以接受Foo&&类型的参数完成构造,整个过程符合C语法和标准规则,不存在合法性问题。

2. 移动构造的调用保证

这种移动构造的调用是有标准层面明确保证的:

  • 根据C++11及后续标准规定,当返回的是函数内的局部自动对象,且该对象类型与函数返回值的底层值类型匹配时,编译器有两种处理方式:要么执行返回值优化(NRVO)直接省略拷贝/移动操作,要么将该局部对象转换为右值,调用移动构造函数。
  • 也就是说,即便编译器没有做返回值优化,标准也要求必须调用移动构造(只要移动构造函数存在且可访问),不会退化为拷贝构造。这是标准强制的行为,并非编译器扩展。

而fn2中显式构造std::optional<Foo>(out)时,因为直接传入左值out,std::optional的构造函数会匹配接受const Foo&的重载,因此调用拷贝构造函数,这和fn的场景规则完全不同。

另外补充:这里移动out是安全的——out作为局部变量,返回后会被销毁,移动它的资源不会导致悬空引用或重复释放的问题,完全符合移动语义的设计初衷。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:22:44