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*/);
}
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.
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.
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.
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.
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.
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; }
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.
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
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.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With