Modern C++

Choosing Between Default Arguments and Overloading in C++

Decisions, decisions. Here's why overloading will keep you out of trouble.

Do you provide default values for one or more arguments, or add overloads of the function that take fewer arguments? This is a design decision every C++ developer faces at some point.

There are more than 50 guidelines relating to functions (and that's not counting sections on special functions like constructors and destructors). They're all good, but I want to show you F.51: Where there is a choice, prefer default arguments over overloading.

Imagine you have a function that performs some sort of calculation with an optional "fudge factor." One way to design the class would be with two functions, like this:

double Offset(double a, double b, double ff);
double Offset(double a, double b);

This will compile happily enough and you might implement it like this:

double Reactor::Offset(double a, double b, double ff)
{
  // Insanely complicated calculations using member variables, a, b and ff
  return whatever;  
}
double Reactor::Offset(double a, double b)
{
  return Offset(a,b,1.0);
}

Anyone reading this code can see that the two-argument version of Offset is using 1.0 for the "fudge factor" and has the same logic as the three-argument version others -- because all it does is call that three-argument version. Of course, there's no requirement that one call the other -- many developers might copy-and-paste between the two versions of the functions, and "simplify" by omitting things like "multiply by 1" or "add zero" completely, making it hard to see the difference between the two functions.

To make it clear what you're doing, and to prevent developers of the future messing up your clear, intentful code by adding lines to one version of Offset but not the other, you could instead use default arguments. You only declare one function:

double Offset2(double a, double b, double ff=1.0);

In the implementation, you cannot repeat the default value of the argument. I like to leave it there as a comment, though, like this:

double Reactor::Offset2(double a, double b, double ff /* = 1.0*/)
{
  // Insanely complicated calculations using member variables, a, b, and ff
  return whatever;  
}

(This is one of my No. 1 uses of comments that start /* and end */, by the way.)

When you design your class with default argument values, there aren't two functions to copy-and-paste the same set of changes between, nor to change one of while forgetting the other. The differences are very clearly called out -- you don't have two sets of logic depending on whether there's a fudge factor or not, you just have this factor that will be 1.0 if a value isn't passed in for it.

You really can't see a difference from code that uses the class. Both Offset and Offset2 can be called with two or three parameters, like this:

Reactor r;
double d;
d = r.Offset(0.0, 2.5, 2.90);
d = r.Offset(0.0, 2.5);
d = r.Offset2(1.0, 3.5, 1.90);
d = r.Offset2(1.0, 3.5);

When it comes to finding code you need to change, never being sure if you're found all of it is a real challenge. Using default arguments reduces the number of functions you need to find, because it reduces the number of overloads. It makes sure that as time goes by, the actual logic of the two overloads with different numbers of arguments don't drift apart, intentionally or accidentally. It makes your code easier to read and maintain from day one, and expresses your intent.

It's a simple guideline to follow that saves you waffling back and forth on a small decision by giving you a simple and consistent rule, and will improve the classes you design.

About the Author

Kate Gregory has been using C++ since before Microsoft had a C++ compiler, and has been paid to program since 1979. Kate runs a small consulting firm in rural Ontario, Canada, and provides mentoring and management consultant services, as well as writing code every week. She has spoken all over the world, written over a dozen books, and helped thousands of developers to be better at what they do. Kate is a Microsoft Regional Director, a Visual C++ MVP, an Imagine Cup judge and mentor, and an active contributor to StackOverflow and StackExchange sites. She develops courses for Pluralsight, primarily on C++ and Visual Studio. Since its founding in 2014 she is the Open Content Chair and speaker for CppCon, the definitive C++ conference.

comments powered by Disqus

Featured

  • Death of the Dev Machine?

    Here's a takeaway from this week's Ignite 2020 event: An advanced Azure cloud portends the death of the traditional, high-powered dev machine packed with computing, memory and storage components.

  • COVID-19 Is Ignite 2020's Elephant in the Room: 'Frankly, It Sucks'

    As in all things of our new reality, there was no escaping the drastic changes in routine caused by the COVID-19 pandemic during Microsoft's big Ignite 2020 developer/IT pro conference, this week shifted to an online-only event after drawing tens of thousands of in-person attendees in years past.

  • Visual Studio 2019 v16.8 Preview Update Adds Codespaces

    To coincide with the Microsoft Ignite 2020 IT pro/developer event, the Visual Studio dev team shipped a new update, Visual Studio 2019 v16.8 Preview 3.1, with the main attraction being support for cloud-hosted Codespaces, now in a limited beta.

  • Speed Lines Graphic

    New for Blazor: Azure Static Web Apps Support

    With Blazor taking the .NET web development world by storm, one of the first announcements during Microsoft's Ignite 2020 developer/IT event was its new support in Azure Static Web Apps.

  • Entity Framework Core 5 RC1 Is Feature Complete, Ready for Production

    The first release candidate for Entity Framework 5 -- Microsoft's object-database mapper for .NET -- has shipped with a go live license, ready for production.

Upcoming Events