• Advertisement
Sign in to follow this  

is it common practice to use default arguments?

This topic is 3348 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

Is it common practice to use default arguments? Just curious, I myself like them and use them all the time, especially with arguments that are only used in special cases and so forth.

Share this post


Link to post
Share on other sites
Advertisement
It's fairly common, yes. But I'm not convinced it's a good idea.

Default arguments create maintenance nightmares, because it means functions now accept various mixes of arguments, and that means that changes in the signature can go undetected by calling code. If you're lucky you'll notice the bogus function call, but real life experience has shown that there's a tendency for these things to go unnoticed for a long time and cause headaches. It also makes reading the code a pain, because it's unclear at the site where the call is being made what the default parameters are doing in that case. You have to go to the actual function to figure out what's happening.

In short, I'd say it's common but it shouldn't be.

Share this post


Link to post
Share on other sites
In a project some time, when I wanted to add a piece of completely new functionality that required an additional parameter to a function, I'd sometimes give a default value to the added argument, so I wouldn't have to change the existing code using that function.

E.g


bool set_cell_value(int row, int col, int value, bool add_to_undo = true);

bool start_new_game(const RawData& data, unsigned diff, bool novice_game = false);


The first apparently reflects a change when the "undo move" feature was added, and the second when I added a "novice mode" which didn't quite follow all the ways of the normal difficulty settings.

I don't believe the design was entirely good (too big classes/functions) and perhaps the novice_game feature could have been implemented via inheritance and some design patterns (had I planned for the possibility upfront).

Another thing is something like that:


struct Coord
{
int x, y;
Coord(): x(0), y(0) {}
Coord(int x, int y): x(x), y(y) {}
};


Some typing could be saved by giving defaults to the constructor, but it doesn't feel right to allow Coord be constructed with one argument. Don't want the following to work:


void foo(const Coord& );

int main()
{
foo(1);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor


struct Coord
{
int x, y;
Coord(): x(0), y(0) {}
Coord(int x, int y): x(x), y(y) {}
};



Why would you want to initialize a structure with default values?

Quote:
Original post by visitor
Don't want the following to work:


void foo(const Coord& );

int main()
{
foo(1);
}


Declare the constructor explicit (assuming you use default args, of course).

Share this post


Link to post
Share on other sites
Quote:

Why would you want to initialize a structure with default values?


Because there is nothing so special about it that someone wouldn't want to have this default-constructed (and yet not left uninitialized)? A class/struct not having a default constructor is a pretty big restriction...

Quote:

Declare the constructor explicit (assuming you use default args, of course).


Right. Still the following might make you wonder whether someone made a mistake or not:


Coord c(1);

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor
Quote:

Why would you want to initialize a structure with default values?


Because there is nothing so special about it that someone wouldn't want to have this default-constructed (and yet not left uninitialized)? A class/struct not having a default constructor is a pretty big restriction...


It is something special. Why would you want a coordinate or vector type default initialized to 0? If you want it to be (0, 0) just write it. But let the default constructor leave uninitialized values. Can a vector be in an invalid state? Members are public so you can assign anything, so anything is valid.

Quote:
Original post by visitor
Quote:

Declare the constructor explicit (assuming you use default args, of course).


Right. Still the following might make you wonder whether someone made a mistake or not:


Coord c(1);


I was thinking more about 1 arg constructor case.

Share this post


Link to post
Share on other sites
I seemed to think that it is the C++ mind-set that you shouldn't have uninitialized variables if you can help it. In C you can't help it (because there are no constructors), in C++ you can by default-initializing it.

If this wasn't so then why does std::pair (which is practically the same thing) default-initialize its members?

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor
I seemed to think that it is the C++ mind-set that you shouldn't have uninitialized variables if you can help it. In C you can't help it (because there are no constructors), in C++ you can by default-initializing it.


No. You shouldn't have an object in an invalid state. For vectors, there are not invalid states. If you don't want an uninitialized vector, initialize it with some meaningful values.

I think we use the word initialize with different meanings. All structs/classes that have public constructors are always initialized. The thing is that basic types are initialized with undefined values. You cannot not initialize a member.

Now, what do you gain by initializing a vector to (0, 0) by default?
If you don't, then e.g. you can create std::vector<Vec2> v(100) that doesn't waste time for default initializing. There must be a reason why basic types are not default-initialized to 0, right?

Share this post


Link to post
Share on other sites
Quote:
Original post by rozz666
It is something special. Why would you want a coordinate or vector type default initialized to 0? If you want it to be (0, 0) just write it. But let the default constructor leave uninitialized values. Can a vector be in an invalid state? Members are public so you can assign anything, so anything is valid.

I understand your reasoning, but I don't agree. I cannot imagine that some random initial values are desirable. They may be technically 'valid', but they can introduce some unexpected behavior. Defaulting to (0, 0) simply makes your coordinate type more reliable. Which is well worth the cost, imo. After all, if default initializing values is a bottleneck, then the real problem is that you're creating way too many instances, way too often...


So, you can either 1) pass initial values as arguments, or 2) rely on an expected default state. Special cases such as random initial values should not be something to worry about: there are more important matters to attend to.

Besides, positions are usually stored as floating point numbers. Those can be NaN or (negative) infinite, so you cannot rely on their validity anyway. And if you're defaulting floats, but not ints, you're not being consistent, which can cause confusion, bugs, issues... in other words, make things easier for yourself by setting reliable, expected default values. Worry about those micro-optimizations later, when and if you actually need them.

Share this post


Link to post
Share on other sites
Quote:

Now, what do you gain by initializing a vector to (0, 0) by default?
If you don't, then e.g. you can create std::vector<Vec2> v(100) that doesn't waste time for default initializing. There must be a reason why basic types are not default-initialized to 0, right?


Again, I was under the impression that in such cases vector uses the default constructor once and then uses the copy constructor to put 100 copies in the vector. If the vector is going to dutifully copy the one uninitialized value over 100 hundred times, what exactly would be the gain of not providing the defaults?

Basic types probably aren't default-initialized because otherwise it would have been hard to win over C programmers :) You can show them that you can have uninitialized variables alright, though user-defined objects are always default-initialized (unless constructor does right thing for primitive types).

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor
Quote:

Now, what do you gain by initializing a vector to (0, 0) by default?
If you don't, then e.g. you can create std::vector<Vec2> v(100) that doesn't waste time for default initializing. There must be a reason why basic types are not default-initialized to 0, right?


Again, I was under the impression that in such cases vector uses the default constructor once and then uses the copy constructor to put 100 copies in the vector. If the vector is going to dutifully copy the one uninitialized value over 100 hundred times, what exactly would be the gain of not providing the defaults?


You are right. std::vector copies the value. But still you can gain using boost::shared_array.

Share this post


Link to post
Share on other sites
When I used C++, I found them common. I liked them and used them a bit.

Now that I use other languages (that don't have them), I don't use them (or method overloads to reproduce the behavior) much at all. Not necessarily because of their lack, but because I've changed much of my design approach to make methods take less arguments. Generally, methods with lots of arguments are doing too much or trying to be too generic.


ps. Unless your class has a specific invariant that prevents it from existing/functioning with a sane default, it should have a sane default.

Share this post


Link to post
Share on other sites
When we talk about other languages, Python goes a step further and not only lets you use default arguments, but also allows you to call them by name in any order. I haven't used Python for anything big, but this is supposed to be good in cases where you have a large number of arguments and you want to use defaults for some (and don't want to worry about the argument order) - e.g GUI's. With C++ you can get somewhat close to this with the Named Parameter Idiom.


def my_function(a = 3, b = 'spam', c = 42):
print '%s: %d-%d' % (b, a, c)

if __name__ == '__main__':
my_function(10, 'ole!')

#Python has no problem if you want to use default for arbitrary argument.
#You can call them by name
my_function(b = 'yippee')
my_function(c = 100, a = 1)

#that's not all, you can also pass the arguments from a dictionary
args = { 'a' : 14, 'c' : 16 }
my_function(**args)

#or from a list (unnamed)
args = [ 0 ]
my_function(*args)
my_function(b = 'crazy_stuff', *args)



Actually when looking at this, I guess that some of the demonstrated function calls might be considered a misuse by the Python community (e.g what would you expect the last to output?!).

Quote:

You are right. std::vector copies the value. But still you can gain using boost::shared_array.


Or just a plain array. But why be so desperate to get uninitialized garbage?

In C, I guess the main rationale why variables should be uninitialized is that the initialization was separated from declaration (and because it puts speed first over safety any time).

C++ at least gives you the means to avoid shooting yourself in the foot, although there are always things you can do to bypass safety mechanisms. E.g, even with defaulting constructor you can get an uninitialized array of Coords bypassing the constructor calls like this:


#include <cstdio>

struct Coord

{

int x, y;

Coord(): x(0), y(0) {}

Coord(int x, int y): x(x), y(y) {}

};

int main()
{
int size = 10;
char* buffer = new char[sizeof(Coord) * size];
Coord* coords = reinterpret_cast<Coord*>(buffer);
for (int i = 0; i != size; ++i) {
std::printf("%d %d\n", coords.x, coords.y);
}
delete [] buffer;
}





[Edited by - visitor on December 22, 2008 11:20:17 AM]

Share this post


Link to post
Share on other sites
I personally don't really like default arguments. With them, I think it makes it a little harder to find out exactly what the function is trying to accomplish and what the end results will be because some of the arguments are hidden. At work I don't use them (or at least haven't so far), and I haven't seen anyone else use them either (but then again I haven't examined their code extremely closely). Anyway, my opinion is don't use them most of the time. On rare occasion they can be useful, but using them too much just hurts readability.

@rozz666: First of all, visitor was giving an example. It's not necessarily code that was meant to be copied and pasted into a program. And besides, why not initialize them to zero? Of course there are times when it makes sense not to, but there are times when it does. The STL complex class initializes its real and imaginary members to zero, which for me makes sense because I consider a point with no magnitude or direction given to be at the origin, or a zero vector, which has no magnitude or direction. You're getting into premature optimization. I'm with Captain P on this one, "if default initializing values is a bottleneck, then the real problem is that you're creating way too many instances, way too often." Besides, in C++ we (should) like to use RAII.

Share this post


Link to post
Share on other sites
Default parameters in C++ are a bit ugly, but they are useful in a commercial setting. Often things change late in a project, and rather than rewriting every single call to a function you need to change, you can add a default parameter instead and only change the relevant ones.

If you have time to properly design your game, it's a bit nicer to do it non-defaulted, but really, it's not that unreadable.

As for uninitialized variables: ALWAYS INITIALIZE VARIABLES. ALWAYS. Tracking down bugs due to unitialized variables sucks. Especially booleans.

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor

Quote:

You are right. std::vector copies the value. But still you can gain using boost::shared_array.


Or just a plain array. But why be so desperate to get uninitialized garbage?

In C, I guess the main rationale why variables should be uninitialized is that the initialization was separated from declaration (and because it puts speed first over safety any time).

C++ at least gives you the means to avoid shooting yourself in the foot, although there are always things you can do to bypass safety mechanisms. E.g, even with defaulting constructor you can get an uninitialized array of Coords bypassing the constructor calls like this:

*** Source Snippet Removed ***


I never said that you shouldn't initialize you variables. However, if you really have to declare a variable and assign a value later (e.g. in arrays), then you don't need any default values. They just don't make sense. You didn't initialize the vector, so it's undefined. std::string by default is empty, because that's a somewhat neutral state (and most efficient). Vectors, however, don't have neutral values.

Share this post


Link to post
Share on other sites
I think it would be better to forbid default constructing vectors rather than allow them to be uninitialised. While a vector doesn't have any invariants so any state is equally valid, I still think that it is better to have a deterministic system. So IMO either "a default vector is 0,0" or "there is no default vector".

Share this post


Link to post
Share on other sites
Quote:
Original post by Promit
Default arguments create maintenance nightmares, because it means functions now accept various mixes of arguments, and that means that changes in the signature can go undetected by calling code. If you're lucky you'll notice the bogus function call, but real life experience has shown that there's a tendency for these things to go unnoticed for a long time and cause headaches. It also makes reading the code a pain, because it's unclear at the site where the call is being made what the default parameters are doing in that case. You have to go to the actual function to figure out what's happening.

I love default arguments, because it lets me write out shorter function calls most of the time while still leaving in extra functionality when I need it. That said, I agree that Promit makes a very good point.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gaiiden
Quote:
Original post by Promit
Default arguments create maintenance nightmares, because it means functions now accept various mixes of arguments, and that means that changes in the signature can go undetected by calling code. If you're lucky you'll notice the bogus function call, but real life experience has shown that there's a tendency for these things to go unnoticed for a long time and cause headaches. It also makes reading the code a pain, because it's unclear at the site where the call is being made what the default parameters are doing in that case. You have to go to the actual function to figure out what's happening.

I love default arguments, because it lets me write out shorter function calls most of the time while still leaving in extra functionality when I need it. That said, I agree that Promit makes a very good point.
I was curious to see if anyone would notice, but this argument is an argument against having lots of overloads of a function that do the same thing, as well. It's just something where you should be aware of both sides and be careful and deliberate in your design decisions.

Share this post


Link to post
Share on other sites
Quote:
Original post by dashurc
Default parameters in C++ are a bit ugly, but they are useful in a commercial setting. Often things change late in a project, and rather than rewriting every single call to a function you need to change, you can add a default parameter instead and only change the relevant ones.

If you have time to properly design your game, it's a bit nicer to do it non-defaulted, but really, it's not that unreadable.

As for uninitialized variables: ALWAYS INITIALIZE VARIABLES. ALWAYS. Tracking down bugs due to unitialized variables sucks. Especially booleans.


D automatically initializes variables. :P

Share this post


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

  • Advertisement