std::optional构造阶段是否含值?示例代码输出的标准界定
std::optional构造与析构阶段has_value()的行为分析
示例代码
#include <iostream> #include <optional> class A { public: A(); ~A(); }; std::optional<A> a; A::A() { std::cout << a.has_value(); } A::~A() { std::cout << a.has_value(); } int main() { a.emplace(); std::cout << a.has_value(); }
问题
我之前碰到过类似场景,意外发现A的构造函数里a居然没有值——这逻辑上说得通,毕竟对象还没完全构造好,我的设计确实也有优化空间,但我在cppreference里没找到对应的标准规定。所以想问问:这段代码的输出应该是什么?这个行为是标准规定、实现定义还是未定义行为?
不同编译器的实际输出
GCC和Clang输出
010,MSVC输出011。
解答
构造阶段的行为(输出第一个0)
这部分是标准规定的。std::optional::emplace()的执行逻辑是:先在optional的内部存储空间构造A对象,等对象构造完成后,才会把optional的"持有值"标记设为true。所以在A的构造函数执行期间,a.has_value()必然返回false,输出0。
main函数中的行为(输出第二个1)
这部分也是标准规定的。A对象构造完成后,optional的标记已经被设为true,此时调用a.has_value()肯定返回true,输出1。
析构阶段的行为(第三个输出的差异)
这部分属于实现定义的行为。C++标准只要求std::optional在销毁时,如果持有值就必须销毁内部对象,但并没有明确规定:是先把"持有值"标记清除,再调用对象的析构函数;还是先调用对象的析构函数,再清除标记。
- GCC和Clang选择了先清除标记,再执行A的析构函数,所以析构时
a.has_value()返回false,输出0,最终整体输出010。 - MSVC则选择先执行A的析构函数,再清除标记,所以析构时
a.has_value()返回true,输出1,最终整体输出011。
两种实现都符合标准,不存在未定义行为。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

