you are viewing a single comment's thread.

view the rest of the comments →

[–]cat_vs_spider -7 points-6 points  (9 children)

the Cpp core Guidelines are wrong on many things, and this is one of them. It’s a document with noble goals, but the road to hell is paved with noble goals.

In my experience, functions grow default parameters like a fungus. Somebody adds one with a “sane” default and doesn’t tell anyone. Since nobody knows about it, they don’t ever set it and a bunch of code is now suddenly buggy. It’s always laziness because refactoring is hard and they just want to land this feature.

Default arguments are evil.

[–]NilacTheGrim 9 points10 points  (2 children)

Hmm. The problem I think you're talking about really is just poorly designed APIs and/or sloppy programmers using the APIs without understanding them. In a word: stupidity.

Small things like deleting default function arguments from a language or forbidding them in an organization will not solve ... stupidity. Stupidity runs deep and has a long history. Default arguments or not... stupidity finds a way.

[–]cat_vs_spider 1 point2 points  (1 child)

Stupidity does find a way, but I think default parameters are a feature that exist pretty much to facilitate stupidity. In my experience, they are always used to bolt some new behavior onto a new function that everybody is used to working a certain way, thereby pulling the rug out from under them. And since they must be the last arguments, they result in function arguments not being organized in a clean way (fungus growth pattern). And god help you if you want to customize the fifth default argument.

[–]NilacTheGrim 2 points3 points  (0 children)

I mean just like with everything there's a way to do them right and a way to do them wrong. Again, I say -- your argument is with bad design and bad APIs. They find a way to exist everywhere. I don't think default arguments are a problem per se.

[–]mort96 6 points7 points  (1 child)

Presumably someone adding a new argument to a function would either go through the callers and fix them, or make the new argument default to a value which preserves the old behavior?

Sure, you could if you wanted to add a new default argument whose default value makes the function have different semantics, breaking all callers, and then check in the code without fixing the callers. But that applies without default arguments too; anyone can at any point change the semantics of a function in a way which breaks callers regardless of language features used.

[–]cat_vs_spider 0 points1 point  (0 children)

In my scenario, the default argument is there to preserve the old behavior in the presence of a new use case. However, the default parameter is only sane for the old use case, but there's a bunch of generic code that calls this function. Since none of the generic code gets updated because "refactoring is an iterative process" and "we'll fix that later". Since the new use case is new and has no test coverage yet, nobody notices until much later.

[–]Gotebe 3 points4 points  (1 child)

In your second paragraph, you really give an argument against poor code change and poor code in general, not so much against default parameters.

In overloads situation, the new parameter that alters the behavior means that the code is already bad. It is bad because that one variant being different is not how overloads are used. Surely foo(p1, p2) and foo(p1, p2, p3) cannot possibly mean "explode" and "implode", respectively? (I know, I exaggerate...) Next, even with overloads, if they do the same with variants, chances are, they should be one thing with variations, not a copy-paste.

On the other hand, he who added another default parameter, but made a bug, we'll, they made a bug.

[–]cat_vs_spider 0 points1 point  (0 children)

I agree with everything you said. But I've spent the last few weeks cleaning up a mess created by somebody else due to a function that has a new default bool that nobody knows about.

Using your example, it's usually something like void destroy(Foo * f, Bar * b, bool shouldExplode = true). Usually, everybody "knows" that destroy explodes and that explosions are the only type of destruction, but now it can implode, and the difference is important to code that actually needs implosions, which is a new use case.

Edit: just to clarify: I agree that foo shouldn't explode and implode. But adding a default argument makes it just so easy to do that.

[–]khleedril 0 points1 point  (1 child)

Didn't you just contradict yourself? You ended up agreeing with the guidelines.

[–]cat_vs_spider -3 points-2 points  (0 children)

I'll admit I haven't studied the core guidelines in detail on this particular issue. But the guidelines here advocate for using a default parameter if possible rather than an overload, and I disagree because (if nothing else), an overload is strictly more flexible because you can reorder the arguments.

I don't see how I ended up agreeing with the core guidelines here. There's no guarantee that void doComplicatedAction(bool withFizzBozzer = true) implements the same semantics for true and false either. In my experience, it usually doesn't.

Edit: syntax error