The only requirement to type parameter of std::optional<T>
class template mentioned in [optional.optional.general] p3 is that type T
is Destructible.
Suppose I have a very restrictive class which meets that requirement:
struct R
{
R(const R&) = delete;
R& operator=(const R&) = delete;
R(R&&) = delete;
R& operator=(R&&) = delete;
// This works thanks to guaranteed copy elision since C++17.
static R create()
{
return R();
}
private:
R() = default;
};
Yet that class is still perfectly usable:
void test_1()
{
R obj = R::create();
/* use obj */
}
The question is: can I really have a usable object of type std::optional<R>
? By "usable" I mean an object that contains value. If yes, then how do I construct that value there?
The following code obviously fails because every constructor of R
is inaccessible:
void test_2()
{
std::optional<R> o(R::create()); // Error: no matching constructor
}
Edit:
Wouldn't it be nice if std::optional
had a constructor that accepted a builder?
template <typename Builder>
optional::optional(disabmiguating_tag, Builder f)
: my_internal_union(f())
{
}
with apropriate constructor of my_internal_union
of course...
You can use a a type with an operator R
:
struct Builder {
operator R() const { return R::create(); }
};
int main() {
std::optional<R> o(Builder{});
}
See live example at Compiler Explorer
This works because the std::optional<R> o
is constructed via the constructor #8 on cppreference, which means that the R
that inhabits o
is constructed as if by calling
R(std::move(builder))
... where builder
is a reference to the Builder{}
we have passed in.
operator R
returns a prvalue, so the returned R::create()
and the constructed R
are the same object, by virtue of C++17 guaranteed copy elision.