[optional.optional.general] p3std::optional<T>
中提到的类模板的类型参数的唯一要求是类型为Destructible。T
假设我有一个非常严格的课程,可以满足以下要求:
class R
{
public:
~R() = default;
// This works thanks to guaranteed copy elision since C++17.
static R create()
{
return R();
}
void do_something()
{
}
private:
R() = default;
R(const R&) = delete;
R& operator=(const R&) = delete;
R(R&&) = delete;
R& operator=(R&&) = delete;
};
但该类仍然可以完全使用:
void test_1()
{
R obj = R::create();
obj.do_something();
}
问题是:我真的能拥有一个可用的类型对象std::optional<R>
吗?“可用”是指包含值的对象。如果是,那么我该如何在其中构造该值?
下面的代码显然会失败,因为的每个构造函数都R
无法访问:
void test_2()
{
std::optional<R> o(R::create()); // Error: no matching constructor
}
编辑:
std::optional
如果有一个接受构建器的构造函数那不是很好吗?
template <typename Builder>
optional::optional(disabmiguating_tag, Builder f)
: my_internal_union(f())
{
}
my_internal_union
当然,使用适当的构造函数......
您可以使用带有 的类型
operator R
:请参阅Compiler Explorer 中的实例
这是有效的,因为
R
居住的o
是通过调用构造的... 其中是我们传入
builder
的引用。Builder{}
operator R
返回一个 prvalue,因此返回的R::create()
和构造的R
是同一个对象,这得益于 C++17 保证的复制省略。我知道问题是c++17,但显然c++23你可以使用这个技巧:
其中我们创建一个虚拟的非空可选(在本例中为
std::optional<int>
)并使用std::optional<T>::transform
在其内部应用一个函数;该函数通过int
返回所需的对象,该对象在为其有效负载保留的内存中创建。R
R::create()
std::optional
由于 C++17强制复制省略,因此它是有效的,因为 lambda 返回类型是
R
,它与 的类型相同R::create()
,而 又是纯右值。我认为这意味着这个R
对象是直接在“最终”位置构造的,即您正在构造的 内部std::optional<R> o
。可能
std::construct_at
来自c++20是这个技巧的核心,但我不认为你可以从外部使用它,因为你无权访问std::optional
存储有效负载的成员,并且你无法通过访问它,.value()
因为那是空的 UBstd::optional
,并且你不能有一个非空的,std::optional<R>
因为这正是我们正在讨论的!