optional<const int> oi = optional<int>(2); // const T != T
optional<unique_ptr<Base>> op = optional<unique_ptr<Derived>>();
// case 1
struct Tool
{
Tool(optional<int>);
};
optional<int> oi = 1;
optional<Tool> ot = oi;
Error or call optional<T>(U const&) ?
// case 2
struct Tool
{
Tool(int); // ctor 1
Tool(optional<int>); // ctor 2
};
optional<int> oi = 1;
optional<Tool> ot = oi;
template <typename T>
class optional
{
optional(T const&);
optional(T &&);
template <typename U>
optional(optional<U> &&) requires !is_convertible<optional<U>, T>::value;
template <typename U>
optional(optional<U> const&) requires !is_convertible<optional<U>, T>::value;
};
struct Tool
{
explicit Tool(int);
};
optional<int> oi = 1;
optional<Tool> ot(oi);
Error or call optional<T>(optional<U>&&)?What about the following example?struct Tool
{
explicit Tool(int);optional<Tool> ot(oi);
};
optional<int> oi = 1;
Error or call optional<T>(optional<U>&&)?
= make_optional(oi); //ok, optional<optional<int>> is unboxed = make_optional(oi); //ok, optional<optional<int>> is unboxedtemplate <typename T>
class optional
{
//from-T
optional(T const&);
optional(T &&);
//unboxing
template <typename U>
optional(optional<U> &&) requires is_construtible<T, U>::value;
template <typename U>
optional(optional<U> const&) requires is_construtible<T, U>::value;
//forwarding
template <typename U>
optional(U&&) requires is_construtible<T, U&&>::value && !is_optional<U>::value;
};
void fun(optional<T> const& v);
void fun(T const& v);
U u;
fun(u);
One problem with having such "from any convertible U" constructor has been discussed in the past. See this post and the subsequent discussion:
https://groups.google.com/a/isocpp.org/d/msg/std-proposals/uK1HfW4YTEU/u_wZWh-2KzMJ
In short, having this constructor can introduce a number of overload resolution ambiguities:void fun(optional<T> const& v);
void fun(T const& v);
U u;
fun(u);
U is convertible to T, so both candidates are viable.
This ambiguity problem may be fixed by making the the function that takes a T an template:
namespace detail
{
void fun(T const&)
}
template<typename U>
requires is_construtible<T, U&&>::value && !is_optional<U>::value
inline void fun(U&& u)
{
return f(std::forward<U>(u));
}
void fun(optional<T> const& t);
To avoid difference in observerable behaviour between code that define only optional<T> overload and the one that has T overload implemented as a template, the template overload should be has the same restrictions as forwarding optional constructor (to preserve preference of unboxing rule).
namespace impl
{
void fun(T const&);
void fun(nullopt_t);
}
//is_optional<nullopt_t>::value should be true
template<typename U>
requires is_construtible<T, U&&>::value && !is_optional<U>::value
inline void fun(U&& u)
{
return impl::fun(std::forward<U>(u));
}
void fun(optional<T> const& t)
{
return t ? impl::fun(*t) : impl::fun(nullopt);
}
This quarantines that both overloads has the same behaviour if t is present.is_value_or_optional_compatible_with functor. That will make implementation of such functors easier from user presepective:namespace impl
{
void fun(T const&);
void fun(nullopt_t);
}template<typename U>
requires is_value_or_optional_compatible_with<U&&, T const&>
inline void fun(U&& u)
{
return handle_value_or_optional<T const&>(
std::forward<U>(u),
[](T const& t) { return impl::fun(t); },
[] { return impl::fun(nullopt); }
}
The example implementation of handle_value_or_optional:
template<typename T, typename U, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional_impl(U&& u, FV&& fv, FN&&)
requires !is_optional_v<decay_t<U>> && is_convertible_v<U&&, T>
{ return std::forward<FV>(fv)(std::forward<U>(u)); }
/* Avoid constucting optional<T> from optional<U> and use prefect forwarding */
template<typename T, typename U, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional_impl(optional<U>&& u, FV&& fv, FN&& fn)
requires is_optional_v<decay_t<U>> && is_convertible_v<U&&, T>
{
return u ? std::forward<FV>(fv)(std::move(*u)) : std::forward<FN>(fn)();
}
template<typename T, typename U, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional_impl(optional<U> const& u, FV&& fv, FN&& fn)
requires is_optional_v<decay_t<U>> && is_convertible_v<U const&, T>
{
return u ? std::forward<FV>(fv)(*u) : std::forward<FN>(fn)();
}
template<typename T, typename U, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional_impl(optional<U>& u, FV&& fv, FN&& fn)
requires is_optional_v<decay_t<U>> && is_convertible_v<U&, T>
{
return u ? std::forward<FV>(fv)(*u) : std::forward<FN>(fn)();
}
/* Avoid if for fun(nullopt) case */
template<typename T, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional_impl(nullopt_t, FV&&, FN&& fn)
{
return std::forward<FN>(fn)();
}
template<typename T, typename U, typename FV, typename FN>
inline decltype(auto) handle_value_or_optional(U&& u, FV&& fv, FN&& fn)
requires is_value_or_optional_compatible_with_v<U&&, T>
{
return handle_value_or_optional_impl<T>(std::forward<U>(u), std::forward<FV>(fv), std::forward<FN>(fn));
}
Since you have already mostly reinvented them (and so did future's .then), can we get convenient syntax for monadic bind already?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Since you have already mostly reinvented them (and so did future's .then), can we get convenient syntax for monadic bind already?
namespace impl
{
void fun(T const&);
void fun(nullopt_t);
}template<typename U>
requires is_value_or_optional_compatible_with<U&&, T const&>
inline void fun(U&& u)
{
return handle_value_or_optional(
std::forward<U>(u),
[](T const& t) { return impl::fun(t); },
[] { return impl::fun(nullopt); });
}