Function argument returning void or non-void typeWhat are POD types in C++?Why do we need virtual functions in C++?Pretty-print C++ STL containersReturn type deduction: What method is preferred?How do I declare a function whose return type is deduced?Template friend function and return type deductionvoid troubles return value to stringC++: return the return value of a void function from a void functionC++ template function that takes a function type with specific return typeVariadic function wrapper for any return type

How does the Earth's center produce heat?

I know that there is a preselected candidate for a position to be filled at my department. What should I do?

When playing Edgar Markov, what is the definition of a "Vampire spell"?

How to patch glass cuts in a bicycle tire?

How to let other coworkers know that I don't share my coworker's political views?

Must a warlock replace spells with new spells of exactly their Pact Magic spell slot level?

How to make the Bass in SATB move more smoothly?

Can we assume that a hash function with high collision resistance also means highly uniform distribution?

Is there a single word meaning "the thing that attracts me"?

Why isn't 'chemically-strengthened glass' made with potassium carbonate to begin with?

Beginner looking to learn/master musical theory and instrumental ability. Where should I begin?

Natural Armour and Weapons

Do photons bend spacetime or not?

Which European Languages are not Indo-European?

Writing style before Elements of Style

Why was this character made Grand Maester?

How was Daenerys able to legitimise this character?

What would prevent living skin from being a good conductor for magic?

Gravitational Force Between Numbers

Dad jokes are fun

“For nothing” = “pour rien”?

Grade-school elementary algebra presented in an abstract-algebra style?

Is there a simple example that empirical evidence is misleading?

Drums and punctuation



Function argument returning void or non-void type


What are POD types in C++?Why do we need virtual functions in C++?Pretty-print C++ STL containersReturn type deduction: What method is preferred?How do I declare a function whose return type is deduced?Template friend function and return type deductionvoid troubles return value to stringC++: return the return value of a void function from a void functionC++ template function that takes a function type with specific return typeVariadic function wrapper for any return type






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








13















I am in middle of writing some generic code for future library. I came across following problem inside template function. Imagine following code:



template<class F>
auto foo(F &&f)
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;



It will work fine unless I pass to it function that returns void like that



foo([]());


Now of course I could use some std::enable_if magic to check return type and make specialization for function returning void look like this:



template<class F, class = /*enable if stuff*/>
void foo(F &&f)
std::forward<F>(f)(/*some args*/);
//do some generic stuff



But that would awfully duplicate code for actually logically equivalent function. Can this be done easily in generic way for both void-returning and non-void-returning functions in elegant way?



EDIT:
there is data dependency between function f() and generic stuff I want to do, so I do not take code like this into account:



template<class F>
auto foo(F &&f)
//do some generic stuff
return std::forward<F>(f)(/*some args*/);










share|improve this question



















  • 1





    offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

    – Marek R
    7 hours ago











  • After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

    – andreee
    7 hours ago






  • 3





    @MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

    – bartop
    7 hours ago











  • OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

    – Marek R
    7 hours ago






  • 1





    related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

    – geza
    7 hours ago

















13















I am in middle of writing some generic code for future library. I came across following problem inside template function. Imagine following code:



template<class F>
auto foo(F &&f)
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;



It will work fine unless I pass to it function that returns void like that



foo([]());


Now of course I could use some std::enable_if magic to check return type and make specialization for function returning void look like this:



template<class F, class = /*enable if stuff*/>
void foo(F &&f)
std::forward<F>(f)(/*some args*/);
//do some generic stuff



But that would awfully duplicate code for actually logically equivalent function. Can this be done easily in generic way for both void-returning and non-void-returning functions in elegant way?



EDIT:
there is data dependency between function f() and generic stuff I want to do, so I do not take code like this into account:



template<class F>
auto foo(F &&f)
//do some generic stuff
return std::forward<F>(f)(/*some args*/);










share|improve this question



















  • 1





    offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

    – Marek R
    7 hours ago











  • After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

    – andreee
    7 hours ago






  • 3





    @MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

    – bartop
    7 hours ago











  • OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

    – Marek R
    7 hours ago






  • 1





    related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

    – geza
    7 hours ago













13












13








13








I am in middle of writing some generic code for future library. I came across following problem inside template function. Imagine following code:



template<class F>
auto foo(F &&f)
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;



It will work fine unless I pass to it function that returns void like that



foo([]());


Now of course I could use some std::enable_if magic to check return type and make specialization for function returning void look like this:



template<class F, class = /*enable if stuff*/>
void foo(F &&f)
std::forward<F>(f)(/*some args*/);
//do some generic stuff



But that would awfully duplicate code for actually logically equivalent function. Can this be done easily in generic way for both void-returning and non-void-returning functions in elegant way?



EDIT:
there is data dependency between function f() and generic stuff I want to do, so I do not take code like this into account:



template<class F>
auto foo(F &&f)
//do some generic stuff
return std::forward<F>(f)(/*some args*/);










share|improve this question
















I am in middle of writing some generic code for future library. I came across following problem inside template function. Imagine following code:



template<class F>
auto foo(F &&f)
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;



It will work fine unless I pass to it function that returns void like that



foo([]());


Now of course I could use some std::enable_if magic to check return type and make specialization for function returning void look like this:



template<class F, class = /*enable if stuff*/>
void foo(F &&f)
std::forward<F>(f)(/*some args*/);
//do some generic stuff



But that would awfully duplicate code for actually logically equivalent function. Can this be done easily in generic way for both void-returning and non-void-returning functions in elegant way?



EDIT:
there is data dependency between function f() and generic stuff I want to do, so I do not take code like this into account:



template<class F>
auto foo(F &&f)
//do some generic stuff
return std::forward<F>(f)(/*some args*/);







c++ templates c++14






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 7 hours ago









max66

41.2k74777




41.2k74777










asked 8 hours ago









bartopbartop

3,5821133




3,5821133







  • 1





    offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

    – Marek R
    7 hours ago











  • After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

    – andreee
    7 hours ago






  • 3





    @MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

    – bartop
    7 hours ago











  • OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

    – Marek R
    7 hours ago






  • 1





    related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

    – geza
    7 hours ago












  • 1





    offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

    – Marek R
    7 hours ago











  • After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

    – andreee
    7 hours ago






  • 3





    @MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

    – bartop
    7 hours ago











  • OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

    – Marek R
    7 hours ago






  • 1





    related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

    – geza
    7 hours ago







1




1





offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

– Marek R
7 hours ago





offtopic: this std::forward is misused. It should be used only if argument can be moved to next function/template. It doesn't break anything here it is just boilerplate.

– Marek R
7 hours ago













After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

– andreee
7 hours ago





After your edit: How can this possibly work with void return types? Either you assign the return value to result or you leave it. auto return = some_void_func() doesn't make any sense.

– andreee
7 hours ago




3




3





@MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

– bartop
7 hours ago





@MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?

– bartop
7 hours ago













OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

– Marek R
7 hours ago





OK fair point, it is possible that someone can define such operator. This is quite unusual, but possible, so std::forward can have sense here.

– Marek R
7 hours ago




1




1





related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

– geza
7 hours ago





related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html

– geza
7 hours ago












4 Answers
4






active

oldest

votes


















11














if you can place the "some generic stuff" in the destructor of a bar class (inside a security try/catch block, if you're not sure that doesn't throw exceptions, as pointed by Drax), you can simply write



template <typename F>
auto foo (F &&f)

bar b;

return std::forward<F>(f)(/*some args*/);



So the compiler compute f(/*some args*/), exec the destructor of b and return the computed value (or nothing).



Observe that return func();, where func() is a function returning void, is perfectly legal.






share|improve this answer




















  • 2





    bonus tip: the "generic stuff" should better not throw anything

    – Drax
    7 hours ago






  • 1





    @Drax - good point; thanks; added in the answer.

    – max66
    7 hours ago


















7














Some specialization, somewhere, is necessary. But the goal here is to avoid specializing the function itself. However, you can specialize a helper class.



Tested with gcc 9.1 with -std=c++17.



#include <type_traits>
#include <iostream>

template<typename T>
struct return_value


T val;

template<typename F, typename ...Args>
return_value(F &&f, Args && ...args)
: valf(std::forward<Args>(args)...)



T value() const

return val;

;

template<>
struct return_value<void>

template<typename F, typename ...Args>
return_value(F &&f, Args && ...args)

f(std::forward<Args>(args)...);


void value() const


;

template<class F>
auto foo(F &&f)

return_value<decltype(std::declval<F &&>()(2, 4))> rf, 2, 4;

// Something

return r.value();


int main()

foo( [](int a, int b) return; );

std::cout << foo( [](int a, int b) return a+b; ) << std::endl;






share|improve this answer























  • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

    – Jarod42
    7 hours ago











  • @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

    – max66
    7 hours ago












  • This incurs an unconditional extra copy.

    – Barry
    6 hours ago











  • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

    – Sam Varshavchik
    5 hours ago











  • @SamVarshavchik That's... not a solution.

    – Barry
    5 hours ago


















2














The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.



struct Void ;


All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:



// normal case: R isn't void
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<!std::is_void<R>::value, int> = 0>
R invoke_void(F&& f, Args&&... args)
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);


// special case: R is void
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<std::is_void<R>::value, int> = 0>
Void invoke_void(F&& f, Args&&... args)
// just call it, since it doesn't return anything
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

// and return Void
return Void;



The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:



template<class F>
auto foo(F &&f)
auto result = invoke_void(std::forward<F>(f), /*some args*/);
//do some generic stuff
return result;



And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]) returning Void instead of void, which isn't much of a cost.



And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.






share|improve this answer






























    0














    In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).



    Not really elegant, to be honest.



    First of all, detect the "true return type" of f (given the arguments)



    using TR_t = std::invoke_result_t<F, As...>;


    Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)



    constexpr bool isVoidTR std::is_same_v<TR_t, void> ;


    Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise



    using FR_t = std::conditional_t<isVoidTR, int, TR_t>;


    Then we define the result value as "fake return type" (so int in void case)



    FR_t result ;


    Then a (void) instruction to avoid a possible "set but not used" warning



    (void)result;


    Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)



    if constexpr ( isVoidTR )
    std::forward<F>(f)(std::forward<As>(args)...);
    else
    result = std::forward<F>(f)(std::forward<As>(args)...);


    After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).



    To conclude, another if constexpr



    if constexpr ( isVoidTR )
    return;
    else
    return result;


    The following is a full compiling C++17 example



    #include <memory>
    #include <type_traits>

    template <typename F, typename ... As>
    auto foo (F && f, As && ... args)

    // true return type
    using TR_t = std::invoke_result_t<F, As...>;

    constexpr bool isVoidTR std::is_same_v<TR_t, void> ;

    // (possibly) fake return type
    using FR_t = std::conditional_t<isVoidTR, int, TR_t>;

    FR_t result ; // is int in case of void

    (void)result; // to avoid "set but not used" warning, in void case

    if constexpr ( isVoidTR )
    std::forward<F>(f)(std::forward<As>(args)...);
    else
    result = std::forward<F>(f)(std::forward<As>(args)...);

    // some generic stuff (potentially depending from result,
    // in non-void cases)

    if constexpr ( isVoidTR )
    return;
    else
    return result;


    int main ()

    foo([]());

    //auto a foo([]()) ; // compilation error: foo() is void

    auto b foo([](auto a0, auto...) return a0; , 1, 2l, 3ll) ;

    static_assert( std::is_same_v<decltype(b), int> );







    share|improve this answer























      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',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      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%2f56256640%2ffunction-argument-returning-void-or-non-void-type%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      11














      if you can place the "some generic stuff" in the destructor of a bar class (inside a security try/catch block, if you're not sure that doesn't throw exceptions, as pointed by Drax), you can simply write



      template <typename F>
      auto foo (F &&f)

      bar b;

      return std::forward<F>(f)(/*some args*/);



      So the compiler compute f(/*some args*/), exec the destructor of b and return the computed value (or nothing).



      Observe that return func();, where func() is a function returning void, is perfectly legal.






      share|improve this answer




















      • 2





        bonus tip: the "generic stuff" should better not throw anything

        – Drax
        7 hours ago






      • 1





        @Drax - good point; thanks; added in the answer.

        – max66
        7 hours ago















      11














      if you can place the "some generic stuff" in the destructor of a bar class (inside a security try/catch block, if you're not sure that doesn't throw exceptions, as pointed by Drax), you can simply write



      template <typename F>
      auto foo (F &&f)

      bar b;

      return std::forward<F>(f)(/*some args*/);



      So the compiler compute f(/*some args*/), exec the destructor of b and return the computed value (or nothing).



      Observe that return func();, where func() is a function returning void, is perfectly legal.






      share|improve this answer




















      • 2





        bonus tip: the "generic stuff" should better not throw anything

        – Drax
        7 hours ago






      • 1





        @Drax - good point; thanks; added in the answer.

        – max66
        7 hours ago













      11












      11








      11







      if you can place the "some generic stuff" in the destructor of a bar class (inside a security try/catch block, if you're not sure that doesn't throw exceptions, as pointed by Drax), you can simply write



      template <typename F>
      auto foo (F &&f)

      bar b;

      return std::forward<F>(f)(/*some args*/);



      So the compiler compute f(/*some args*/), exec the destructor of b and return the computed value (or nothing).



      Observe that return func();, where func() is a function returning void, is perfectly legal.






      share|improve this answer















      if you can place the "some generic stuff" in the destructor of a bar class (inside a security try/catch block, if you're not sure that doesn't throw exceptions, as pointed by Drax), you can simply write



      template <typename F>
      auto foo (F &&f)

      bar b;

      return std::forward<F>(f)(/*some args*/);



      So the compiler compute f(/*some args*/), exec the destructor of b and return the computed value (or nothing).



      Observe that return func();, where func() is a function returning void, is perfectly legal.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited 7 hours ago

























      answered 8 hours ago









      max66max66

      41.2k74777




      41.2k74777







      • 2





        bonus tip: the "generic stuff" should better not throw anything

        – Drax
        7 hours ago






      • 1





        @Drax - good point; thanks; added in the answer.

        – max66
        7 hours ago












      • 2





        bonus tip: the "generic stuff" should better not throw anything

        – Drax
        7 hours ago






      • 1





        @Drax - good point; thanks; added in the answer.

        – max66
        7 hours ago







      2




      2





      bonus tip: the "generic stuff" should better not throw anything

      – Drax
      7 hours ago





      bonus tip: the "generic stuff" should better not throw anything

      – Drax
      7 hours ago




      1




      1





      @Drax - good point; thanks; added in the answer.

      – max66
      7 hours ago





      @Drax - good point; thanks; added in the answer.

      – max66
      7 hours ago













      7














      Some specialization, somewhere, is necessary. But the goal here is to avoid specializing the function itself. However, you can specialize a helper class.



      Tested with gcc 9.1 with -std=c++17.



      #include <type_traits>
      #include <iostream>

      template<typename T>
      struct return_value


      T val;

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)
      : valf(std::forward<Args>(args)...)



      T value() const

      return val;

      ;

      template<>
      struct return_value<void>

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)

      f(std::forward<Args>(args)...);


      void value() const


      ;

      template<class F>
      auto foo(F &&f)

      return_value<decltype(std::declval<F &&>()(2, 4))> rf, 2, 4;

      // Something

      return r.value();


      int main()

      foo( [](int a, int b) return; );

      std::cout << foo( [](int a, int b) return a+b; ) << std::endl;






      share|improve this answer























      • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

        – Jarod42
        7 hours ago











      • @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

        – max66
        7 hours ago












      • This incurs an unconditional extra copy.

        – Barry
        6 hours ago











      • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

        – Sam Varshavchik
        5 hours ago











      • @SamVarshavchik That's... not a solution.

        – Barry
        5 hours ago















      7














      Some specialization, somewhere, is necessary. But the goal here is to avoid specializing the function itself. However, you can specialize a helper class.



      Tested with gcc 9.1 with -std=c++17.



      #include <type_traits>
      #include <iostream>

      template<typename T>
      struct return_value


      T val;

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)
      : valf(std::forward<Args>(args)...)



      T value() const

      return val;

      ;

      template<>
      struct return_value<void>

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)

      f(std::forward<Args>(args)...);


      void value() const


      ;

      template<class F>
      auto foo(F &&f)

      return_value<decltype(std::declval<F &&>()(2, 4))> rf, 2, 4;

      // Something

      return r.value();


      int main()

      foo( [](int a, int b) return; );

      std::cout << foo( [](int a, int b) return a+b; ) << std::endl;






      share|improve this answer























      • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

        – Jarod42
        7 hours ago











      • @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

        – max66
        7 hours ago












      • This incurs an unconditional extra copy.

        – Barry
        6 hours ago











      • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

        – Sam Varshavchik
        5 hours ago











      • @SamVarshavchik That's... not a solution.

        – Barry
        5 hours ago













      7












      7








      7







      Some specialization, somewhere, is necessary. But the goal here is to avoid specializing the function itself. However, you can specialize a helper class.



      Tested with gcc 9.1 with -std=c++17.



      #include <type_traits>
      #include <iostream>

      template<typename T>
      struct return_value


      T val;

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)
      : valf(std::forward<Args>(args)...)



      T value() const

      return val;

      ;

      template<>
      struct return_value<void>

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)

      f(std::forward<Args>(args)...);


      void value() const


      ;

      template<class F>
      auto foo(F &&f)

      return_value<decltype(std::declval<F &&>()(2, 4))> rf, 2, 4;

      // Something

      return r.value();


      int main()

      foo( [](int a, int b) return; );

      std::cout << foo( [](int a, int b) return a+b; ) << std::endl;






      share|improve this answer













      Some specialization, somewhere, is necessary. But the goal here is to avoid specializing the function itself. However, you can specialize a helper class.



      Tested with gcc 9.1 with -std=c++17.



      #include <type_traits>
      #include <iostream>

      template<typename T>
      struct return_value


      T val;

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)
      : valf(std::forward<Args>(args)...)



      T value() const

      return val;

      ;

      template<>
      struct return_value<void>

      template<typename F, typename ...Args>
      return_value(F &&f, Args && ...args)

      f(std::forward<Args>(args)...);


      void value() const


      ;

      template<class F>
      auto foo(F &&f)

      return_value<decltype(std::declval<F &&>()(2, 4))> rf, 2, 4;

      // Something

      return r.value();


      int main()

      foo( [](int a, int b) return; );

      std::cout << foo( [](int a, int b) return a+b; ) << std::endl;







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered 7 hours ago









      Sam VarshavchikSam Varshavchik

      64.6k53783




      64.6k53783












      • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

        – Jarod42
        7 hours ago











      • @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

        – max66
        7 hours ago












      • This incurs an unconditional extra copy.

        – Barry
        6 hours ago











      • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

        – Sam Varshavchik
        5 hours ago











      • @SamVarshavchik That's... not a solution.

        – Barry
        5 hours ago

















      • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

        – Jarod42
        7 hours ago











      • @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

        – max66
        7 hours ago












      • This incurs an unconditional extra copy.

        – Barry
        6 hours ago











      • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

        – Sam Varshavchik
        5 hours ago











      • @SamVarshavchik That's... not a solution.

        – Barry
        5 hours ago
















      "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

      – Jarod42
      7 hours ago





      "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.

      – Jarod42
      7 hours ago













      @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

      – max66
      7 hours ago






      @Jarod42 - but the other answer has a great limit: if the "some generic stuff" needs the value computed by f() (if not void) I don't see a way to avoid a specialization somewhere. I suspect that Sam's answer is better than mine.

      – max66
      7 hours ago














      This incurs an unconditional extra copy.

      – Barry
      6 hours ago





      This incurs an unconditional extra copy.

      – Barry
      6 hours ago













      An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

      – Sam Varshavchik
      5 hours ago





      An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.

      – Sam Varshavchik
      5 hours ago













      @SamVarshavchik That's... not a solution.

      – Barry
      5 hours ago





      @SamVarshavchik That's... not a solution.

      – Barry
      5 hours ago











      2














      The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.



      struct Void ;


      All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:



      // normal case: R isn't void
      template <typename F, typename... Args,
      typename R = std::invoke_result_t<F, Args...>,
      std::enable_if_t<!std::is_void<R>::value, int> = 0>
      R invoke_void(F&& f, Args&&... args)
      return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);


      // special case: R is void
      template <typename F, typename... Args,
      typename R = std::invoke_result_t<F, Args...>,
      std::enable_if_t<std::is_void<R>::value, int> = 0>
      Void invoke_void(F&& f, Args&&... args)
      // just call it, since it doesn't return anything
      std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

      // and return Void
      return Void;



      The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:



      template<class F>
      auto foo(F &&f)
      auto result = invoke_void(std::forward<F>(f), /*some args*/);
      //do some generic stuff
      return result;



      And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]) returning Void instead of void, which isn't much of a cost.



      And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.






      share|improve this answer



























        2














        The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.



        struct Void ;


        All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:



        // normal case: R isn't void
        template <typename F, typename... Args,
        typename R = std::invoke_result_t<F, Args...>,
        std::enable_if_t<!std::is_void<R>::value, int> = 0>
        R invoke_void(F&& f, Args&&... args)
        return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);


        // special case: R is void
        template <typename F, typename... Args,
        typename R = std::invoke_result_t<F, Args...>,
        std::enable_if_t<std::is_void<R>::value, int> = 0>
        Void invoke_void(F&& f, Args&&... args)
        // just call it, since it doesn't return anything
        std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

        // and return Void
        return Void;



        The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:



        template<class F>
        auto foo(F &&f)
        auto result = invoke_void(std::forward<F>(f), /*some args*/);
        //do some generic stuff
        return result;



        And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]) returning Void instead of void, which isn't much of a cost.



        And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.






        share|improve this answer

























          2












          2








          2







          The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.



          struct Void ;


          All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:



          // normal case: R isn't void
          template <typename F, typename... Args,
          typename R = std::invoke_result_t<F, Args...>,
          std::enable_if_t<!std::is_void<R>::value, int> = 0>
          R invoke_void(F&& f, Args&&... args)
          return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);


          // special case: R is void
          template <typename F, typename... Args,
          typename R = std::invoke_result_t<F, Args...>,
          std::enable_if_t<std::is_void<R>::value, int> = 0>
          Void invoke_void(F&& f, Args&&... args)
          // just call it, since it doesn't return anything
          std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

          // and return Void
          return Void;



          The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:



          template<class F>
          auto foo(F &&f)
          auto result = invoke_void(std::forward<F>(f), /*some args*/);
          //do some generic stuff
          return result;



          And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]) returning Void instead of void, which isn't much of a cost.



          And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.






          share|improve this answer













          The best way to do this, in my opinion, is to actually change the way you call your possibly-void-returning functions. Basically, we change the ones that return void to instead return some class type Void that is, for all intents and purposes, the same thing and no users really are going to care.



          struct Void ;


          All we need to do is to wrap the invocation. The following uses C++17 names (std::invoke and std::invoke_result_t) but they're all implementable in C++14 without too much fuss:



          // normal case: R isn't void
          template <typename F, typename... Args,
          typename R = std::invoke_result_t<F, Args...>,
          std::enable_if_t<!std::is_void<R>::value, int> = 0>
          R invoke_void(F&& f, Args&&... args)
          return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);


          // special case: R is void
          template <typename F, typename... Args,
          typename R = std::invoke_result_t<F, Args...>,
          std::enable_if_t<std::is_void<R>::value, int> = 0>
          Void invoke_void(F&& f, Args&&... args)
          // just call it, since it doesn't return anything
          std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

          // and return Void
          return Void;



          The advantage of doing it this way is that you can just directly write the code you wanted to write to begin with, in the way you wanted to write it:



          template<class F>
          auto foo(F &&f)
          auto result = invoke_void(std::forward<F>(f), /*some args*/);
          //do some generic stuff
          return result;



          And you don't have to either shove all your logic in a destructor or duplicate all of your logic by doing specialization. At the cost of foo([]) returning Void instead of void, which isn't much of a cost.



          And then if Regular Void is ever adopted, all you have to do is swap out invoke_void for std::invoke.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered 6 hours ago









          BarryBarry

          190k21340625




          190k21340625





















              0














              In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).



              Not really elegant, to be honest.



              First of all, detect the "true return type" of f (given the arguments)



              using TR_t = std::invoke_result_t<F, As...>;


              Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)



              constexpr bool isVoidTR std::is_same_v<TR_t, void> ;


              Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise



              using FR_t = std::conditional_t<isVoidTR, int, TR_t>;


              Then we define the result value as "fake return type" (so int in void case)



              FR_t result ;


              Then a (void) instruction to avoid a possible "set but not used" warning



              (void)result;


              Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)



              if constexpr ( isVoidTR )
              std::forward<F>(f)(std::forward<As>(args)...);
              else
              result = std::forward<F>(f)(std::forward<As>(args)...);


              After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).



              To conclude, another if constexpr



              if constexpr ( isVoidTR )
              return;
              else
              return result;


              The following is a full compiling C++17 example



              #include <memory>
              #include <type_traits>

              template <typename F, typename ... As>
              auto foo (F && f, As && ... args)

              // true return type
              using TR_t = std::invoke_result_t<F, As...>;

              constexpr bool isVoidTR std::is_same_v<TR_t, void> ;

              // (possibly) fake return type
              using FR_t = std::conditional_t<isVoidTR, int, TR_t>;

              FR_t result ; // is int in case of void

              (void)result; // to avoid "set but not used" warning, in void case

              if constexpr ( isVoidTR )
              std::forward<F>(f)(std::forward<As>(args)...);
              else
              result = std::forward<F>(f)(std::forward<As>(args)...);

              // some generic stuff (potentially depending from result,
              // in non-void cases)

              if constexpr ( isVoidTR )
              return;
              else
              return result;


              int main ()

              foo([]());

              //auto a foo([]()) ; // compilation error: foo() is void

              auto b foo([](auto a0, auto...) return a0; , 1, 2l, 3ll) ;

              static_assert( std::is_same_v<decltype(b), int> );







              share|improve this answer



























                0














                In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).



                Not really elegant, to be honest.



                First of all, detect the "true return type" of f (given the arguments)



                using TR_t = std::invoke_result_t<F, As...>;


                Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)



                constexpr bool isVoidTR std::is_same_v<TR_t, void> ;


                Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise



                using FR_t = std::conditional_t<isVoidTR, int, TR_t>;


                Then we define the result value as "fake return type" (so int in void case)



                FR_t result ;


                Then a (void) instruction to avoid a possible "set but not used" warning



                (void)result;


                Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)



                if constexpr ( isVoidTR )
                std::forward<F>(f)(std::forward<As>(args)...);
                else
                result = std::forward<F>(f)(std::forward<As>(args)...);


                After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).



                To conclude, another if constexpr



                if constexpr ( isVoidTR )
                return;
                else
                return result;


                The following is a full compiling C++17 example



                #include <memory>
                #include <type_traits>

                template <typename F, typename ... As>
                auto foo (F && f, As && ... args)

                // true return type
                using TR_t = std::invoke_result_t<F, As...>;

                constexpr bool isVoidTR std::is_same_v<TR_t, void> ;

                // (possibly) fake return type
                using FR_t = std::conditional_t<isVoidTR, int, TR_t>;

                FR_t result ; // is int in case of void

                (void)result; // to avoid "set but not used" warning, in void case

                if constexpr ( isVoidTR )
                std::forward<F>(f)(std::forward<As>(args)...);
                else
                result = std::forward<F>(f)(std::forward<As>(args)...);

                // some generic stuff (potentially depending from result,
                // in non-void cases)

                if constexpr ( isVoidTR )
                return;
                else
                return result;


                int main ()

                foo([]());

                //auto a foo([]()) ; // compilation error: foo() is void

                auto b foo([](auto a0, auto...) return a0; , 1, 2l, 3ll) ;

                static_assert( std::is_same_v<decltype(b), int> );







                share|improve this answer

























                  0












                  0








                  0







                  In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).



                  Not really elegant, to be honest.



                  First of all, detect the "true return type" of f (given the arguments)



                  using TR_t = std::invoke_result_t<F, As...>;


                  Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)



                  constexpr bool isVoidTR std::is_same_v<TR_t, void> ;


                  Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise



                  using FR_t = std::conditional_t<isVoidTR, int, TR_t>;


                  Then we define the result value as "fake return type" (so int in void case)



                  FR_t result ;


                  Then a (void) instruction to avoid a possible "set but not used" warning



                  (void)result;


                  Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)



                  if constexpr ( isVoidTR )
                  std::forward<F>(f)(std::forward<As>(args)...);
                  else
                  result = std::forward<F>(f)(std::forward<As>(args)...);


                  After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).



                  To conclude, another if constexpr



                  if constexpr ( isVoidTR )
                  return;
                  else
                  return result;


                  The following is a full compiling C++17 example



                  #include <memory>
                  #include <type_traits>

                  template <typename F, typename ... As>
                  auto foo (F && f, As && ... args)

                  // true return type
                  using TR_t = std::invoke_result_t<F, As...>;

                  constexpr bool isVoidTR std::is_same_v<TR_t, void> ;

                  // (possibly) fake return type
                  using FR_t = std::conditional_t<isVoidTR, int, TR_t>;

                  FR_t result ; // is int in case of void

                  (void)result; // to avoid "set but not used" warning, in void case

                  if constexpr ( isVoidTR )
                  std::forward<F>(f)(std::forward<As>(args)...);
                  else
                  result = std::forward<F>(f)(std::forward<As>(args)...);

                  // some generic stuff (potentially depending from result,
                  // in non-void cases)

                  if constexpr ( isVoidTR )
                  return;
                  else
                  return result;


                  int main ()

                  foo([]());

                  //auto a foo([]()) ; // compilation error: foo() is void

                  auto b foo([](auto a0, auto...) return a0; , 1, 2l, 3ll) ;

                  static_assert( std::is_same_v<decltype(b), int> );







                  share|improve this answer













                  In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).



                  Not really elegant, to be honest.



                  First of all, detect the "true return type" of f (given the arguments)



                  using TR_t = std::invoke_result_t<F, As...>;


                  Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)



                  constexpr bool isVoidTR std::is_same_v<TR_t, void> ;


                  Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise



                  using FR_t = std::conditional_t<isVoidTR, int, TR_t>;


                  Then we define the result value as "fake return type" (so int in void case)



                  FR_t result ;


                  Then a (void) instruction to avoid a possible "set but not used" warning



                  (void)result;


                  Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)



                  if constexpr ( isVoidTR )
                  std::forward<F>(f)(std::forward<As>(args)...);
                  else
                  result = std::forward<F>(f)(std::forward<As>(args)...);


                  After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).



                  To conclude, another if constexpr



                  if constexpr ( isVoidTR )
                  return;
                  else
                  return result;


                  The following is a full compiling C++17 example



                  #include <memory>
                  #include <type_traits>

                  template <typename F, typename ... As>
                  auto foo (F && f, As && ... args)

                  // true return type
                  using TR_t = std::invoke_result_t<F, As...>;

                  constexpr bool isVoidTR std::is_same_v<TR_t, void> ;

                  // (possibly) fake return type
                  using FR_t = std::conditional_t<isVoidTR, int, TR_t>;

                  FR_t result ; // is int in case of void

                  (void)result; // to avoid "set but not used" warning, in void case

                  if constexpr ( isVoidTR )
                  std::forward<F>(f)(std::forward<As>(args)...);
                  else
                  result = std::forward<F>(f)(std::forward<As>(args)...);

                  // some generic stuff (potentially depending from result,
                  // in non-void cases)

                  if constexpr ( isVoidTR )
                  return;
                  else
                  return result;


                  int main ()

                  foo([]());

                  //auto a foo([]()) ; // compilation error: foo() is void

                  auto b foo([](auto a0, auto...) return a0; , 1, 2l, 3ll) ;

                  static_assert( std::is_same_v<decltype(b), int> );








                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 1 hour ago









                  max66max66

                  41.2k74777




                  41.2k74777



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56256640%2ffunction-argument-returning-void-or-non-void-type%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Siegen Nawigatsjuun

                      Log på Navigationsmenu

                      Log på Navigationsmenu