Make a function accepting an optional to accept a non-optional?

Make a function accepting an optional to accept a non-optional?



I'm trying to write syntactic sugar, in a monad-style, over std::optional. Please consider:


std::optional


template<class T>
void f(std::optional<T>)



As is, this function cannot be called with a non-optional T1 (e.g. an int), even though there exists a conversion from T to std::optional<T>2.


T


int


T


std::optional<T>



Is there a way to make f accept an std::optional<T> or a T (converted to an optional at the caller site), without defining an overload3?


f


std::optional<T>


T



1)f(0): error: no matching function for call to 'f(int)' and note: template argument deduction/substitution failed, (demo).
2) Because template argument deduction doesn't consider conversions.
3) Overloading is an acceptable solution for a unary function, but starts to be an annoyance when you have binary functions like operator+(optional, optional), and is a pain for ternary, 4-ary, etc. functions.


f(0)


error: no matching function for call to 'f(int)'


note: template argument deduction/substitution failed


operator+(optional, optional)





have it take a T and then just check if it's an optional and if it's not, make it one. constexpr if can do that super easy if you're using c++17.
– xaxxon
Aug 21 at 9:00






@xaxxon that's worth an answer
– YSC
Aug 21 at 9:01





Can you clarify what you mean by “in a monad-style”? Because as it stands your question suggests you are interested in some kind of automatic conversion or overload, and both answers do something along these lines. But this has nothing whatsoever to do with monads.
– Konrad Rudolph
Aug 21 at 10:43






@KonradRudolph The mention of monads was just to give a context. I want, provided F=λxy., an optional(x) and an optional(y), return an optional(F(xy)). For such a definition to be useful and let its user chain expressions, it needs to accept both optionals and scalars.
– YSC
Aug 21 at 11:39



F=λxy.


optional(x)


optional(y)


optional(F(xy))





The problem with this definition is that, unlike a monad, it does not compose. How would your function handle a std::optional<std::optional<T>>? Whereas if you had a monad lifting operator you would simply lift(F) and wouldn’t have this problem.
– Konrad Rudolph
Aug 21 at 12:49


std::optional<std::optional<T>>


lift(F)




4 Answers
4



Another version. This one doesn't involve anything:


template <typename T>
void f(T&& t)
std::optional opt = std::forward<T>(t);



Class template argument deduction already does the right thing here. If t is an optional, the copy deduction candidate will be preferred and we get the same type back. Otherwise, we wrap it.


t


optional





Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
– YSC
Aug 21 at 13:15





Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
– LThode
Aug 21 at 15:34





@LThode Very much no.
– Barry
Aug 21 at 15:37





@Barry -- a-ha, I see what you mean now -- I've never bumped into class template argument deduction before!
– LThode
Aug 21 at 15:40





This lacks overload resolution features; is there a way to test if std::optional opt = std::forward<T>(t) would compile?
– Yakk - Adam Nevraumont
Aug 21 at 17:42


std::optional opt = std::forward<T>(t)



Instead of taking optional as argument take deductible template parameter:


template<class T>
struct is_optional : std::false_type;

template<class T>
struct is_optional<std::optional<T>> : std::true_type;

template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val)
return std::forward<T>(val);


template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val)
return std::forward<T>(val) ;


template<class T>
void f(T &&t)
auto opt = to_optional(std::forward<T>(t));


int main()
f(1);
f(std::optional<int>(1));



Live example



This uses one of my favorite type traits, which can check any all-type template against a type to see if it's the template for it.


#include <iostream>
#include <type_traits>
#include <optional>


template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type ;

template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type ;

template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;

template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;


template <typename T>
void f(T && t)
auto optional_t = [&]
if constexpr (is_template_for_v<std::optional, T>)
return t;
else
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));

();
(void)optional_t;


int main()
int i = 5;
std::optional<int> oi5;

f(i);
f(oi);



https://godbolt.org/z/HXgoEE





Really nice trait you've got here. Do you know why std::decay is necessary?
– YSC
Aug 21 at 9:22



std::decay





@YSC Without std::decay it would not work automagically with types like std::optional<int> & and related ones
– bartop
Aug 21 at 9:31


std::decay


std::optional<int> &



Another version. This one doesn't involve writing traits:


template <typename T>
struct make_optional_t
template <typename U>
auto operator()(U&& u) const
return std::optional<T>(std::forward<U>(u));

;

template <typename T>
struct make_optional_t<std::optional<T>>
template <typename U>
auto operator()(U&& u) const
return std::forward<U>(u);

;

template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;

template <typename T>
void f(T&& t)
auto opt = make_optional<T>(std::forward<T>(t));





@YSC Shrug. Wrote it a different way instead.
– Barry
Aug 21 at 15:05






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)