I would like to use the optional idiom inside my constexpr function to easily clarify if the variable is set or not.
What I have tried with std::experimental::optional:
constexpr bool call()
{
std::experimental::optional<bool> r;
r = true; // Error
// Similar error with:
// r = std::experimental::optional<bool>(true);
if (!r)
{
return false;
}
return *r;
}
I get the error: call to non-constexpr function - so the assignment is not possible, because this operation cannot be constexpr (Example).
But if I implement my own (very ugly, just for example) optional class, it works, because I don´t implement the assignment operator/constructor explicit.
template<typename T>
struct optional
{
bool m_Set;
T m_Data;
constexpr optional() :
m_Set(false), m_Data{}
{
}
constexpr optional(T p_Data) :
m_Set(true), m_Data(p_Data)
{
}
explicit constexpr operator bool()
{
return m_Set;
}
constexpr T operator *()
{
return m_Data;
}
};
How could I use std::..::optional in the same context with assignment inside constexpr functions?
Basically, you can't. The problem with your simple implementation is that it requires T
be default-constructible - if this is not the case, this won't work.
To get around this, most implementation use either a union
or some (suitably aligned) storage that can hold a T
. If you are passed a T
in the constructor, then all well and good, you can initialize this directly (hence it will be constexpr
). However, the tradeoff here is that when calling operator=
, copying the value across may require a placement-new call, which cannot be constexpr
.
For example, from LLVM:
template <class _Up,
class = typename enable_if
<
is_same<typename remove_reference<_Up>::type, value_type>::value &&
is_constructible<value_type, _Up>::value &&
is_assignable<value_type&, _Up>::value
>::type
>
_LIBCPP_INLINE_VISIBILITY
optional&
operator=(_Up&& __v)
{
if (this->__engaged_)
this->__val_ = _VSTD::forward<_Up>(__v);
else
{
// Problem line is below - not engaged -> need to call
// placement new with the value passed in.
::new(_VSTD::addressof(this->__val_)) value_type(_VSTD::forward<_Up>(__v));
this->__engaged_ = true;
}
return *this;
}
As for why placement new is not constexpr
, see here.
This is not possible, as explained in n3527:
Making
optional
a literal typeWe propose that
optional<T>
be a literal type for trivially destructible T's.constexpr optional<int> oi{5}; static_assert(oi, ""); // ok static_assert(oi != nullopt, ""); // ok static_assert(oi == oi, ""); // ok int array[*oi]; // ok: array of size 5
Making
optional<T>
a literal-type in general is impossible: the destructor cannot be trivial because it has to execute an operation that can be conceptually described as:~optional() { if (is_engaged()) destroy_contained_value(); }
It is still possible to make the destructor trivial for
T
's which provide a trivial destructor themselves, and we know an efficient implementation of suchoptional<T>
with compile-time interface — except for copy constructor and move constructor — is possible. Therefore we propose that for trivially destructibleT
's alloptional<T>
's constructors, except for move and copy constructors, as well as observer functions are constexpr. The sketch of reference implementation is provided in this proposal.
In other words, it's not possible to assign a value to r
even if you mark it as constexpr. You must initialize it in the same line.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With