I have several void functions (lets call them foo and bar) that share the same void function cleanup that, well, cleans up after them if they mess up:
#include <iostream>
void cleanup() { std::cout << "doing cleanup" << std::endl; }
void foo(int & i) {
if(i == 0) { return cleanup(); }
--i;
if(i == 0) { return cleanup(); }
++i;
}
void bar(int & i) {
if(i == 0) { return cleanup(); }
++i;
if(i == 0) { return cleanup(); }
--i;
}
int main() {
int i = 0;
foo(i);
bar(i);
return 0;
}
cpp.sh happily compiles and runs the code.
Thanks to the answer to this question I know that I can return an object of type void. But I don't know if it applies to returning void return values.
What do you think, does the code comply with the standard?
And would you rather return dummy integers to make the code more readable? Or would the useless return values make the code harder to read?
edit: I feel like I need to add some clarification as to why e.g. 'cleanup(); return;' is not a solution. The actual code is more complex than the example and depending on where I leave the function some other stuff happens after the cleanup() call. 'return cleanup();' is just a convenient way of not putting all the stuff behind it in conditionals in instances where I can/have to leave immediately.
If the function has a void return type, this behavior is okay, but may be considered poor style. Use a plain return statement to make your intent clear. As a good engineering practice, always specify a return type for your functions. If a return value isn't required, declare the function to have void return type.
Void functions are created and used just like value-returning functions except they do not return a value after the function executes. In lieu of a data type, void functions use the keyword "void." A void function performs a task, and then control returns back to the caller--but, it does not return a value.
Paragraph 2
The expr-or-braced-init-list of a return statement is called its operand. A return statement with no operand shall be used only in a function whose return type is cv void, a constructor (12.1), or a destructor (12.4). A return statement with an operand of type void shall be used only in a function whose return type is cv void. A return statement with any other operand shall be used only in a function whose return type is not cv void; the return statement initializes the object or reference to be returned by copy-initialization (8.5) from the operand.
Questions:
But I don't know if it applies to returning void return values.
Yes its perfectly valid to return a void expression from a function that return void. This becomes very handy in templated code so you don't have to special case void functions.
What do you think, does the code comply with the standard?
Yes absolutely.
And would you rather return dummy integers to make the code more readable? Or would the useless return values make the code harder to read?
That's totally up to you and your aesthetics. Does it make the code look more readable to you (or do you have coding guidelines that you need to follow). Personally I would not return dummy values (as the user may expect some meaning from them).
This is useful for perfect forwarding. Imagine that you have a functor of type (template-parameter), so the return type can potentially be anything. This permission to return a value of type void
allows you to call the functor and expose its return value to your caller, without having to write separate code for the case of no return value.
template<typename Functor, typename... Args>
auto forward(Functor what_to_call, Args... args) -> decltype(what_to_call(std::forward<Args>(args)...))
{
return what_to_call(std::forward<Args>(args)...);
}
That's already pretty messy, but if not for the ability to propagate void return types using the return
keyword, you'd need two variations controlled by enable_if
.
template<typename Functor, typename... Args>
auto forward(Functor what_to_call, Args... args) -> enable_if<is_same_type<decltype(what_to_call(std::forward<Args>(args)...)), void>::value, void>::type
{
what_to_call(std::forward<Args>(args)...);
return;
}
template<typename Functor, typename... Args>
auto forward(Functor what_to_call, Args... args) -> enable_if<!is_same_type<decltype(what_to_call(std::forward<Args>(args)...)), void>::value, decltype(what_to_call(std::forward<Args>(args)...))>::type
{
return what_to_call(std::forward<Args>(args)...);
}
So, return function_returning_void();
is designed for special circumstances where it is useful. Don't feel that you have to return a dummy object of type void
or any other type everywhere just because it is possible. For example, don't do return void();
or return -1;
when simply return;
will do.
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