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

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
39
down vote

favorite
10












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 T1 (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.







share|improve this question
















  • 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., 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














up vote
39
down vote

favorite
10












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 T1 (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.







share|improve this question
















  • 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., 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












up vote
39
down vote

favorite
10









up vote
39
down vote

favorite
10






10





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 T1 (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.







share|improve this question












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 T1 (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.









share|improve this question











share|improve this question




share|improve this question










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, 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












  • 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., 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







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












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.






share|improve this answer
















  • 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 if std::optional opt = std::forward<T>(t) would compile?
    – Yakk - Adam Nevraumont
    Aug 21 at 17:42

















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






share|improve this answer





























    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






    share|improve this answer






















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







    • 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

















    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));






    share|improve this answer






















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










    Your Answer





    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    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






























    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.






    share|improve this answer
















    • 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 if std::optional opt = std::forward<T>(t) would compile?
      – Yakk - Adam Nevraumont
      Aug 21 at 17:42














    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.






    share|improve this answer
















    • 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 if std::optional opt = std::forward<T>(t) would compile?
      – Yakk - Adam Nevraumont
      Aug 21 at 17:42












    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.






    share|improve this answer












    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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 if std::optional opt = std::forward<T>(t) would compile?
      – Yakk - Adam Nevraumont
      Aug 21 at 17:42












    • 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 if std::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












    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






    share|improve this answer


























      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






      share|improve this answer
























        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






        share|improve this answer














        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







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Aug 21 at 9:16

























        answered Aug 21 at 9:11









        bartop

        2,153823




        2,153823




















            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






            share|improve this answer






















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







            • 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














            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






            share|improve this answer






















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







            • 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












            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






            share|improve this answer














            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







            share|improve this answer














            share|improve this answer



            share|improve this answer








            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 why std::decay is necessary?
              – YSC
              Aug 21 at 9:22







            • 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
















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







            • 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















            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










            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));






            share|improve this answer






















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














            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));






            share|improve this answer






















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












            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));






            share|improve this answer














            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));







            share|improve this answer














            share|improve this answer



            share|improve this answer








            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
















            • @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












             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            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













































































            這個網誌中的熱門文章

            How to combine Bézier curves to a surface?

            Mutual Information Always Non-negative

            Why am i infinitely getting the same tweet with the Twitter Search API?