• Advertisement
Sign in to follow this  

Why not use return type "void"?

This topic is 2299 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I taught myself C in my early teens and every reference I could find had functions prefaced with "void" if they did not return a value. As such, I thought it was standard practice to do so and that void implied no return value. I have always followed this practice.

Now, I'm teaching myself C++ and I am finding references stating quite bluntly that you should never a return type of void. OK, I do understand that void is a valid type, but if there is no return statement in the function and it is very clear that nothing can and/or should be returned, why not use void?

Even more confusing is that I've read that not having a return type implies a type of "int". So, this would mean that if I do not want a function to return a value and do not include a return type in the definition, it is implied that the function *could* return an int. huh.gif

I'm lost here. Can someone explain this for me?

Share this post


Link to post
Share on other sites
Advertisement

I taught myself C in my early teens and every reference I could find had functions prefaced with "void" if they did not return a value. As such, I thought it was standard practice to do so and that void implied no return value. I have always followed this practice.

Now, I'm teaching myself C++ and I am finding references stating quite bluntly that you should never a return type of void. OK, I do understand that void is a valid type, but if there is no return statement in the function and it is very clear that nothing can and/or should be returned, why not use void?

Even more confusing is that I've read that not having a return type implies a type of "int". So, this would mean that if I do not want a function to return a value and do not include a return type in the definition, it is implied that the function *could* return an int. huh.gif

I'm lost here. Can someone explain this for me?


It's a recommended practice; a dubious one at that. The thought is that functions should do something. They take some input and provide some output. By returning void, you're not visibly doing anything which is frowned upon by those people.

Using void is fine. Function side effects have their own concerns, but they're not something you should worry about while learning a language.

Share this post


Link to post
Share on other sites
Returning void is perfectly fine. If there is nothing to return there is nothing to return. They are most likely saying return at least a boolean to say true or false, if the function actually worked correctly. Sometimes you have early breaks in functions such as checking for null and returning early so it does not crash.

Share this post


Link to post
Share on other sites

Returning void is perfectly fine. If there is nothing to return there is nothing to return. They are most likely saying return at least a boolean to say true or false, if the function actually worked correctly. Sometimes you have early breaks in functions such as checking for null and returning early so it does not crash.


That makes sense, however, it is never stated as such. I always read: "Never use a return type of void."

As Telastyn stated, it is dubious. This is especially true in C++, hence the confusion.

Here is the matrix header for a project I am working on:

[source]

#ifndef __MATRIX_H__
#define __MATRIX_H__

#include "math defs.h"
#include "vector.h"

/*
* |0 4 8 12|
* m[0..15] = |1 5 9 13|
* |2 6 10 14|
* |3 7 11 15|
*/

class matrix{
public:
float GetMatrixElement(unsigned int row,unsigned int column) const {if(row < 4 || column < 4) return(m[(column << 2) + row]);}
void SetMatrixElement(unsigned int row,unsigned int column,float _m) {if(row < 4 || column < 4) m[(column << 2) + row] = _m;}

float Get_m00() const {return(m[0]);}
float Get_m01() const {return(m[4]);}
float Get_m02() const {return(m[8]);}
float Get_m03() const {return(m[12]);}

float Get_m10() const {return(m[1]);}
float Get_m11() const {return(m[5]);}
float Get_m12() const {return(m[9]);}
float Get_m13() const {return(m[13]);}

float Get_m20() const {return(m[2]);}
float Get_m21() const {return(m[6]);}
float Get_m22() const {return(m[10]);}
float Get_m23() const {return(m[14]);}

float Get_m30() const {return(m[3]);}
float Get_m31() const {return(m[7]);}
float Get_m32() const {return(m[11]);}
float Get_m33() const {return(m[15]);}

void Set_m00(float _m) {m[0] = _m;}
void Set_m01(float _m) {m[4] = _m;}
void Set_m02(float _m) {m[8] = _m;}
void Set_m03(float _m) {m[12] = _m;}

void Set_m10(float _m) {m[1] = _m;}
void Set_m11(float _m) {m[5] = _m;}
void Set_m12(float _m) {m[6] = _m;}
void Set_m13(float _m) {m[13] = _m;}

void Set_m20(float _m) {m[2] = _m;}
void Set_m21(float _m) {m[6] = _m;}
void Set_m22(float _m) {m[10] = _m;}
void Set_m23(float _m) {m[14] = _m;}

void Set_m30(float _m) {m[3] = _m;}
void Set_m31(float _m) {m[7] = _m;}
void Set_m32(float _m) {m[11] = _m;}
void Set_m33(float _m) {m[15] = _m;}

void IdentityMatrix();
void RotationMatrix(float angle,float x,float y,float z);
void MatrixTransformVector(vector *vec);
void MatrixMultiply(matrix _m);
private:
float m[16];

const unsigned int m00 = 0;
const unsigned int m01 = 4;
const unsigned int m02 = 8;
const unsigned int m03 = 12;

const unsigned int m10 = 1;
const unsigned int m11 = 5;
const unsigned int m12 = 9;
const unsigned int m13 = 13;

const unsigned int m20 = 2;
const unsigned int m21 = 6;
const unsigned int m22 = 10;
const unsigned int m23 = 14;

const unsigned int m30 = 3;
const unsigned int m31 = 7;
const unsigned int m32 = 11;
const unsigned int m33 = 15;
};

#endif
[/source]


It is clear that the functions "IdentityMatrix", "RotationMatrix", "MatrixTransformVector" and "MatrixMultiply" operate on the class variable "m". As such, they cannot possibly return a value, other than maybe a bool. So, from Telastyn's reasoning, the functions are incorrect to return a type of void as they should be returning something. However, these are proper C++ function definitions.


What reference is stating that ?


The book "Teach yourself C++ is 24 hours" which I no longer have and then various references and quotes online, including from members on this site. Sorry, I cannot be more specific. I never thought it worth the time to save a link just for this.


Having functions/procedures/methods with a void returntype however is not necessarily wrong if you are doing procedural programming. (If you are doing functional programming then void functions can be considered bad since they will always be non pure)

void pointers on the other hand is a almost always an awful idea in C++, especially if you use them like you would in C.


Good to know.

Share this post


Link to post
Share on other sites

I would say you should probably disregard anything else such a source tells you.

There is no "default int" in C++. A function must have a return type, and there is nothing wrong with having a return type of "void". The standard C++ library itself provides many functions that have a return value of type "void."


Thanks. I figured as much. I am also willing to admit that I may have misunderstood the intent of the comment(s). I just wanted to make sure I wasn't doing anything incorrectly.

Share this post


Link to post
Share on other sites
Your source should be shot. According to their logic, the C++ standard is violating this "rule" (std::swap, std::fill, std::replace, std::reverse, std::string::resize, std::vector::clear, etc, etc, etc...). [font="Courier New"]void[/font] is a part of the C++ language for a reason, and it should be used if it is needed. They obviously have no clue what they're talking about.

As others have said, that part about the "default int" type is BS as well.

Share this post


Link to post
Share on other sites
As part of the recommended process of splitting up complicated bits of code into multiple functions, you're likely to end up with functions/methods which perform some operation on whatever structure without needing to return anything. Now you COULD, for eaxmple, have a std::vector resize() function that created a separate copy of the resized vector instead of performing the operation in-place. But that would be horribly inefficient. There's ongoing debate over where to draw the line. Should you provide a matrix multiply function that returns the resulting matrix by value, or should it use an output reference parameter? Return Value optimisation will often eliminate the overhead anyway, but if you never did any operation in-place, you'd have a pretty significant performance impact.

Share this post


Link to post
Share on other sites
The curious thing about those advices is that they makes a lot more sense in legacy C (I mean C89 and before). The default return value of a C89 function is indeed int and if you do not write the return value in that language the compiler interpret it as int. In the early days of C, void wasn't supported (before the ANSI standard). If you programmed in C back then, the advice to avoid to use void made therefore sense. Moreover, the return value is the main method to return errors conditions to the user and some C style standard advocate the use of int as the only acceptable return value (with the use of the parameters for returning value). But those advices are just BS in C++ and probably also "modern" C.

Share this post


Link to post
Share on other sites
I have also questioned the whole no void function positions. It is simply better practice to return a value. There are 2 major reasons why. 1. If a function returns no value, chances are you dont really need that function. Secondly, always returning a value act a layer of error checking. If you dont want a function to return an object, it is good practice to make it return a bool or int for error checking purposes. Nothing is particularly wrong with void functions, sometimes it is the correct choice. One thing I like to do is have my catch statement return a negative value to identify where my code went wrong, return 1, and true if the function exited gracefully.

Share this post


Link to post
Share on other sites

1. If a function returns no value, chances are you dont really need that function.

False. The whole reason for a function is to reuse a piece of code. Sure, I could do my own, in place version of std::swap. But there are two big problems with that. One, I have to keep creating a temporary variable every time I want to swap, which pollutes my local block with unnecessary variable names. Two, it does not allow certain data types to have a specialized version of the function. Doing an in place version of std::swap with a temp, swapping two std::strings is a bad idea because the temp has to allocate its buffer, perform a deep copy of the other string, then the first string has to have a deep copy of the second string, then the second string has to get a deep copy of the temp. Lots of deep copying, and an unnecessary allocation (which will be followed by a deallocation), plus any allocated memory whose size needs to be appropriately adjusted.

OR I could just use std::swap, which can be specialized to simply swap the pointers of the two strings. Done. No unnecessary allocations/deallocations, no dealing with temps that pollute my block, etc.

And it's not necessarily a bad thing that std::swap is void. I mean really, swapping two integers, you really need to do error checking for that? You seriously want std::swap to return true indicating "Yeah! I can swap two integers successfully!" Really? Yes, error checking is an important aspect of programming, but constantly returning unnecessary booleans is just annoying, plus its not always efficient. C++ was intentionally designed to avoid unnecessary error checking in order to increase its speed. Of course, that means you, the programmer, have to do your own error checking if you want, and if you don't you're more likely to blow your leg off, but insisting a function that swaps two ints return true is silly.


Secondly, always returning a value act a layer of error checking.

Sometimes. Exceptions are incredibly useful. Unfortunately, some platforms or other conditions makes it so exceptions cannot be used. If exceptions can't be used, then yeah, everything that could fail that you need to know about should inform you somehow (usually through a return value). Or sometimes a particular function can perform faster if it returns an error code rather than throwing an exception. However, sometimes a return value just isn't necessary. std::swap(int, int) comes to mind as a very obvious example.


[edit]

ApochPiQ ninja'd me, though we cover different points.

[edit edit]

I've noticed I mention a few times points about speed. Please, for the love of all things holy, don't think I'm encouraging premature optimization or unnecessary micro optimization. Speed is actually my least concern while we're discussing these things.

Share this post


Link to post
Share on other sites
Function returning void in C++ == pure action. Imho, it is an essential part of OOP.

For instance, one can close a door or one can check to see if the door is closed, but one cannot do both at exactly the same time.

However, the door itself can give feedback as you are closing it and that is where a return value would come into play.

Any other way of looking at it would break the 'modularity' of the action, wouldn't it?

(caveat: this may in fact be crazy-talk, as I am both over-caffeinated and over-tired)

Share this post


Link to post
Share on other sites

Always returning a success/failure value is a surefire recipe to have code littered with junk like this:

bool ApplyDamage(int hitpoints)
{
MyHP -= hitpoints;
return true;
}

This butts up against another design rule of thumb for return types: a return value should have an unambiguous meaning for it's type and the function name. For ApplyDamage() what would a bool return type mean? It could mean whether or not there were any errors in the inputs (trying to apply negative damage). It could mean whether or not the damage pierced armor. It could mean whether or not the target survived the attack. A void return type has the unique virtue that its meaning is always unambiguous. Similarly, a properly defined enum could be unambiguous in this kind of situation.

Share this post


Link to post
Share on other sites

I taught myself C in my early teens and every reference I could find had functions prefaced with "void" if they did not return a value. As such, I thought it was standard practice to do so and that void implied no return value. I have always followed this practice.

Now, I'm teaching myself C++ and I am finding references stating quite bluntly that you should never a return type of void. OK, I do understand that void is a valid type, but if there is no return statement in the function and it is very clear that nothing can and/or should be returned, why not use void?


1) Take those reference
2) Burn them
3) with fire
4) repeatedly

Then use recognized C++ materials to learn.

Share this post


Link to post
Share on other sites
Weird discussion.
I had never heard of anyone saying you should never return "void", there are plenty of examples were void is the obvious choice, as has already been pointed out.
If you have a value to return, you return it, if not, void. Simple as that.

"void*" on the other hand, is a very different matter.
I can't think of any sensible reason to return a void*, unless your OOP design is broken.

Share this post


Link to post
Share on other sites

"void*" on the other hand, is a very different matter.
I can't think of any sensible reason to return a void*, unless your OOP design is broken.


std::allocator.

Then there's that big CS thing called type erasure, thought I always felt it's overhyped.

Share this post


Link to post
Share on other sites
Another fairly common use is in APIs that allow the user to associate data with API objects. Then you frequently have a GetUserData()/SetUserData() pair that works with void pointers.

Share this post


Link to post
Share on other sites
Antheus, when does std::allocator return void*? I suppose it would if one were to ask for the template for it, but who would do that, when would they do that, and why?

Share this post


Link to post
Share on other sites

Antheus, when does std::allocator return void*? I suppose it would if one were to ask for the template for it, but who would do that, when would they do that, and why?


I seemed to recall that allocate or construct operate on void *, but apparently they're typed. I probably incorrectly remembered that underlying allocation returns void and must be cast there before returning.

Share this post


Link to post
Share on other sites

Another fairly common use is in APIs that allow the user to associate data with API objects. Then you frequently have a GetUserData()/SetUserData() pair that works with void pointers.


Fair point, didn't think of getters/setters for that.
Have used it as arguments to callbacks and such for userdata, wich is pretty much the same use-case.

Maybe at the base of it, the allocator uses a void* but thats a really special case...

Share this post


Link to post
Share on other sites
Though, speaking strictly from an OOP point of view, having void* userdata pointers is a bit sketchy...

But it definitely is one of those times where it can be useful to bend the rules a bit.

Share this post


Link to post
Share on other sites
Returning void* can also be used for other stuff than storing an address, for example operator void* on streams which can be used for checking the state of the stream.

Share this post


Link to post
Share on other sites

Returning void* can also be used for other stuff than storing an address, for example operator void* on streams which can be used for checking the state of the stream.


With just this information, that sounds like pretty horrible design, to completely change functionality of core concepts like that...
What is the benefit of solving it this way?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement