在构造期间,std::optional是否包含一个值?

5

请查看以下代码作为一个例子:

#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没有值。这可能是件好事,因为对象没有完全构建完成,我的设计可以改进。但是我在cppreference中没有找到任何关于它应该按标准如何运行的证据。 所以我的问题是(主要是出于好奇):上面的示例代码应该输出什么,并且是指定的还是实现定义(或UB)? 有趣的是,上面的代码打印出(剧透): 对于GCC和Cland,为010,对于MSVC为011

编译器输出显示,在构造时没有值(并且MSVC在析构时包含值),所有编译器都同意这一点。 - apple apple
直到A::A()完成,才会有一个A。如果A::A()抛出异常,那么a从未持有过值。我不确定是否已经指定了这一点,但这是我直觉上期望的行为,也是最容易实现的行为。我只期望在emplace返回后has_valuetrue。此外,在emplace返回之前可能无法调用a.value(),支持has_value应始终为false - François Andrieux
2
https://cplusplus.github.io/LWG/issue2414 - Language Lawyer
2
在执行emplace构造函数时,std::optional是否应该包含其值? - Language Lawyer
1个回答

2
据我所知,当前标准草案(由https://timsong-cpp.github.io/cppwp/提供)并没有明确规定,但对于我来说,MSVC的行为是指定的(无论在销毁期间是否需要这种行为都是可疑的):
首先,emplace 包含 以下文本

效果:调用 *this = nullopt。然后使用 std::forward(args) 直接非列表初始化所包含的值....

...

备注:如果在调用 T 的构造函数期间抛出异常,则 *this 不包含任何值,并且先前的 *val(如果有)已被销毁。

按照 nullopt 赋值的方式将 has_value 设置为 false,然后在构造过程中将其设置为 true(当该操作未指定时),最后如果构造失败并引发异常,则重置它,这将是一种错误的解读。
在销毁期间的行为是这个

效果:如果is_­trivially_­destructible_­v != true并且*this包含一个值,则调用val->T::~T()

如果需要更改has_value,则没有提及,如果需要指定,则应该这样做。

我不确定你所说的“MSVC行为对我来说是预期的”是什么意思,因为它们只在销毁时有所不同,在构造期间没有一个是“true”。 - apple apple
@appleapple 我已经编辑过了,更接近我的意思。你说得对,要知道那种行为是否是有意的,就必须深入研究设计文档和委员会的记录。 - Nicolas Hamblenne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接