Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function argument returning void or non-void type

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*/);
}
like image 204
bartop Avatar asked May 22 '19 12:05

bartop


People also ask

Can function return the type void?

A void function can return A void function cannot return any values. But we can use the return statement. It indicates that the function is terminated. It increases the readability of code.

Why are functions returning non-void assignable to function returning void?

TypeScript FAQ: Why are functions returning non- void assignable to function returning void ? Because returning something when nothing is expected does no harm, but returning the wrong thing when something is expected does harm. There's no compelling reason to perform checks to prevent the former.

What is non-void return type?

In functions that have non-void return types (dont worry, we'll cover "void" later), a value is returned by the function when we call it. We know a function has a non-void return type by looking for a return type in the first line of the function definition or a return statement in the body of the function.

What is void and non-void function?

A function that does not return a value is called a non-value returning function (or a void function). Void functions don't need a return statement. A void function will automatically return to the caller at the end of the function. No return statement is required.

Can you use a void function without a return statement?

You may or may not use the return statement, as there is no return value. Even without the return statement, control will return to the caller automatically at the end of the function. A good utilization of a void function would be to print a header/footer to a screen or file.

What is a function with arguments but no return value?

Function with arguments but no return value : When a function has arguments, it receive any data from the calling function but it returns no values. Syntax : Function declaration : void function ( int ); Function call : function( x ); Function definition: void function( int x ) { statements; }

What is a void function in C++?

Void functions are known as Non-Value Returning functions. They 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. Void functions do not have a return type, but they can do return values.

How to return a value from a function that never returns?

You created int function that never returns a value. If you have a function that you don't want any value to be returned, just make it void. To make your code work simply change function type from int to void


2 Answers

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.

like image 92
max66 Avatar answered Sep 19 '22 07:09

max66


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;
}
like image 33
Sam Varshavchik Avatar answered Sep 17 '22 07:09

Sam Varshavchik