隐式转换为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
相关产品推荐
相关产品推荐

