Make a function accepting an optional to accept a non-optional?
Clash Royale CLAN TAG#URR8PPP
up vote
39
down vote
favorite
I'm trying to write syntactic sugar, in a monad-style, over std::optional
. Please consider:
template<class T>
void f(std::optional<T>)
As is, this function cannot be called with a non-optional T
1 (e.g. an int
), even though there exists a conversion from T
to std::optional<T>
2.
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?
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.
c++ templates c++17 optional template-deduction
 |Â
show 3 more comments
up vote
39
down vote
favorite
I'm trying to write syntactic sugar, in a monad-style, over std::optional
. Please consider:
template<class T>
void f(std::optional<T>)
As is, this function cannot be called with a non-optional T
1 (e.g. an int
), even though there exists a conversion from T
to std::optional<T>
2.
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?
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.
c++ templates c++17 optional template-deduction
12
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
8
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
3
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, providedF=ûxy.
, anoptional(x)
and anoptional(y)
, return anoptional(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
The problem with this definition is that, unlike a monad, it does not compose. How would your function handle astd::optional<std::optional<T>>
? Whereas if you had a monad lifting operator you would simplylift(F)
and wouldnâÂÂt have this problem.
â Konrad Rudolph
Aug 21 at 12:49
 |Â
show 3 more comments
up vote
39
down vote
favorite
up vote
39
down vote
favorite
I'm trying to write syntactic sugar, in a monad-style, over std::optional
. Please consider:
template<class T>
void f(std::optional<T>)
As is, this function cannot be called with a non-optional T
1 (e.g. an int
), even though there exists a conversion from T
to std::optional<T>
2.
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?
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.
c++ templates c++17 optional template-deduction
I'm trying to write syntactic sugar, in a monad-style, over std::optional
. Please consider:
template<class T>
void f(std::optional<T>)
As is, this function cannot be called with a non-optional T
1 (e.g. an int
), even though there exists a conversion from T
to std::optional<T>
2.
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?
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.
c++ templates c++17 optional template-deduction
asked Aug 21 at 8:56
YSC
16.4k33886
16.4k33886
12
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
8
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
3
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, providedF=ûxy.
, anoptional(x)
and anoptional(y)
, return anoptional(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
The problem with this definition is that, unlike a monad, it does not compose. How would your function handle astd::optional<std::optional<T>>
? Whereas if you had a monad lifting operator you would simplylift(F)
and wouldnâÂÂt have this problem.
â Konrad Rudolph
Aug 21 at 12:49
 |Â
show 3 more comments
12
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
8
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
3
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, providedF=ûxy.
, anoptional(x)
and anoptional(y)
, return anoptional(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
The problem with this definition is that, unlike a monad, it does not compose. How would your function handle astd::optional<std::optional<T>>
? Whereas if you had a monad lifting operator you would simplylift(F)
and wouldnâÂÂt have this problem.
â Konrad Rudolph
Aug 21 at 12:49
12
12
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
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
8
8
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
3
3
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
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
@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
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
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
 |Â
show 3 more comments
4 Answers
4
active
oldest
votes
up vote
34
down vote
accepted
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.
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
@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
1
This lacks overload resolution features; is there a way to test ifstd::optional opt = std::forward<T>(t)
would compile?
â Yakk - Adam Nevraumont
Aug 21 at 17:42
 |Â
show 2 more comments
up vote
15
down vote
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
add a comment |Â
up vote
13
down vote
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 whystd::decay
is necessary?
â YSC
Aug 21 at 9:22
2
@YSC Withoutstd::decay
it would not work automagically with types likestd::optional<int> &
and related ones
â bartop
Aug 21 at 9:31
add a comment |Â
up vote
5
down vote
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
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
34
down vote
accepted
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.
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
@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
1
This lacks overload resolution features; is there a way to test ifstd::optional opt = std::forward<T>(t)
would compile?
â Yakk - Adam Nevraumont
Aug 21 at 17:42
 |Â
show 2 more comments
up vote
34
down vote
accepted
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.
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
@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
1
This lacks overload resolution features; is there a way to test ifstd::optional opt = std::forward<T>(t)
would compile?
â Yakk - Adam Nevraumont
Aug 21 at 17:42
 |Â
show 2 more comments
up vote
34
down vote
accepted
up vote
34
down vote
accepted
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.
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.
answered Aug 21 at 13:11
Barry
166k18281518
166k18281518
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
@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
1
This lacks overload resolution features; is there a way to test ifstd::optional opt = std::forward<T>(t)
would compile?
â Yakk - Adam Nevraumont
Aug 21 at 17:42
 |Â
show 2 more comments
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
@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
1
This lacks overload resolution features; is there a way to test ifstd::optional opt = std::forward<T>(t)
would compile?
â Yakk - Adam Nevraumont
Aug 21 at 17:42
2
2
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
Even though this is not code golf, I've got the feeling this answer deserve the checkmark :D
â YSC
Aug 21 at 13:15
1
1
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
Good line of thinking; however, doesn't opt need to be defined as std::optional<T> here?
â LThode
Aug 21 at 15:34
7
7
@LThode Very much no.
â Barry
Aug 21 at 15:37
@LThode Very much no.
â Barry
Aug 21 at 15:37
3
3
@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
@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
1
1
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
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
 |Â
show 2 more comments
up vote
15
down vote
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
add a comment |Â
up vote
15
down vote
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
add a comment |Â
up vote
15
down vote
up vote
15
down vote
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
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
edited Aug 21 at 9:16
answered Aug 21 at 9:11
bartop
2,153823
2,153823
add a comment |Â
add a comment |Â
up vote
13
down vote
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 whystd::decay
is necessary?
â YSC
Aug 21 at 9:22
2
@YSC Withoutstd::decay
it would not work automagically with types likestd::optional<int> &
and related ones
â bartop
Aug 21 at 9:31
add a comment |Â
up vote
13
down vote
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 whystd::decay
is necessary?
â YSC
Aug 21 at 9:22
2
@YSC Withoutstd::decay
it would not work automagically with types likestd::optional<int> &
and related ones
â bartop
Aug 21 at 9:31
add a comment |Â
up vote
13
down vote
up vote
13
down vote
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
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
edited Aug 25 at 9:28
YSC
16.4k33886
16.4k33886
answered Aug 21 at 9:17
xaxxon
13.6k42658
13.6k42658
Really nice trait you've got here. Do you know whystd::decay
is necessary?
â YSC
Aug 21 at 9:22
2
@YSC Withoutstd::decay
it would not work automagically with types likestd::optional<int> &
and related ones
â bartop
Aug 21 at 9:31
add a comment |Â
Really nice trait you've got here. Do you know whystd::decay
is necessary?
â YSC
Aug 21 at 9:22
2
@YSC Withoutstd::decay
it would not work automagically with types likestd::optional<int> &
and related ones
â bartop
Aug 21 at 9:31
Really nice trait you've got here. Do you know why
std::decay
is necessary?â YSC
Aug 21 at 9:22
Really nice trait you've got here. Do you know why
std::decay
is necessary?â YSC
Aug 21 at 9:22
2
2
@YSC Without
std::decay
it would not work automagically with types like std::optional<int> &
and related onesâ bartop
Aug 21 at 9:31
@YSC Without
std::decay
it would not work automagically with types like std::optional<int> &
and related onesâ bartop
Aug 21 at 9:31
add a comment |Â
up vote
5
down vote
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
add a comment |Â
up vote
5
down vote
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
add a comment |Â
up vote
5
down vote
up vote
5
down vote
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));
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));
edited Aug 21 at 15:05
answered Aug 21 at 12:10
Barry
166k18281518
166k18281518
@YSC Shrug. Wrote it a different way instead.
â Barry
Aug 21 at 15:05
add a comment |Â
@YSC Shrug. Wrote it a different way instead.
â Barry
Aug 21 at 15:05
@YSC Shrug. Wrote it a different way instead.
â Barry
Aug 21 at 15:05
@YSC Shrug. Wrote it a different way instead.
â Barry
Aug 21 at 15:05
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51945067%2fmake-a-function-accepting-an-optional-to-accept-a-non-optional%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
12
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
8
@xaxxon that's worth an answer
â YSC
Aug 21 at 9:01
3
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.
, anoptional(x)
and anoptional(y)
, return anoptional(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
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 simplylift(F)
and wouldnâÂÂt have this problem.â Konrad Rudolph
Aug 21 at 12:49