Function argument returning void or non-void type

Related searches

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

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 a function that returns void like:

foo([](){});

Now, of course, I could use some std::enable_if magic to check the return type and perform specialization for a function returning void that looks 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 functions. Can this be done easily in a generic way for both void-returning and non-void-returning functions in a 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*/);
}

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.

Functions with (non-void) Return Types, In functions that have non-void return types (dont worry, we'll cover "void" We know a function has a non-void return type by looking for a return type in which includes a call to the makeCourse function using arguments "COMP" and 110. Void functions are “void” due to the fact that they are not supposed to return values. True, but not completely. We cannot return values but there is something we can surely return from void functions. Some of cases are listed below.

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)
        : val{f(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))> r{f, 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;
}

Functions 2: Void (NonValue-Returning) Functions, Void function: does not have return type; Uses keyword void in function header; Call to void Void Function Definition Using Value Parameters Example: //Void� In the program, we have function named fun1 which has no argument and no return type ( void is the return type - that means, function will not return anything). Within the function fun1 we are declaring an array, calculating sum of its elements and printing the sum.

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.

return statement in C/C++ with Examples, Not using return statement in void return type function: When a function does not return anything, the void return type is used. So if there is a void return type in the function definition, then there will be no return statement inside that function (generally). Example: C. Function with no arguments but returns a value : There could be occasions where we may need to design functions that may not take any arguments but returns a value to the calling function. A example for this is getchar function it has no parameters but it returns an integer an integer type data that represents a character.

Java - Methods, Parameter List − The list of parameters, it is the type, order, and number of parameters of a This method is a void method, which does not return any value . Now in the prototype definition (line No 3) of the function we can see the return value as Void. Void means it does not return anything to the calling function. [box]Types of Function Calling : Function with Argument but No Return Type

Function exit paths should have appropriate return values, Functions without parameters should be declared with parameter type "void" User-defined types should not be passed as variadic arguments. Bug� Void Function returns None. # In Python, void functions are slightly different than functions found in C, C++ or Java. If the function body doesn't have any return statement then a special value None is returned when the function terminates. In Python, None is a literal of type NoneType which used to denote absence of a value. It is commonly assigned to a variable to indicate that the variable does not points to any object.

[ Note: the parameter-declaration-clause is used to convert the arguments specified on the function call; see 5.2.2. — end note] If the parameter-declaration-clause is empty, the function takes no arguments. A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an

The second part is the return type. We make it clear which is the return type by using an arrow (=>) between the parameters and the return type. As mentioned before, this is a required part of the function type, so if the function doesn’t return a value, you would use void instead of leaving it off.

Comments
  • 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.
  • 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.
  • @MarekR what if function object with rvalue only overload of operator() is passed? Will it work without forward?
  • 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.
  • related proposal: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0146r1.html
  • bonus tip: the "generic stuff" should better not throw anything
  • And what if it does throw something? What if you need to propagate that exception?
  • @Barry - yes: this is another limit of this solution.
  • "Some specialization, somewhere, is necessary." not with a Finally raii code, as proposed in another answer.
  • @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.
  • This incurs an unconditional extra copy.
  • An extra copy could be avoided by having foo() return the helper object itself. Which will necessitate changing the callers.
  • @SamVarshavchik That's... not a solution.