Default Arguments
Here is a function that logs a message to the console. With each message, it also needs to log the severity level ("INFO", "WARNING", "ERROR", etc.):
void logMessage(std::string message, std::string level) { std::cout << "[" << level << "] " << message << "\n";}Now suppose 90% of the messages logged with this function are just informational, so we are passing "INFO", over and over:
logMessage("Server started", "INFO");logMessage("Connected", "INFO");logMessage("Cache cleared", "INFO");logMessage("Disk almost full", "WARNING");This kind of repetition is annoying, and it’s also wasted effort. Noise like that can be reduced using default arguments.
Giving a parameter a default value
A default value can be assigned to a parameter by using = in the function’s parameter list:
Now both function calls, on lines 9 and 10, work correctly.
If a call omits an argument (like on line 9), the compiler will detect it and insert the default value at the call site during compilation.
There are a few rules to keep in mind when using default arguments.
Rule 1: Defaults have to trail
Once a parameter has a default argument, all parameters after it are also required to have one. So, you can’t do this:
// This will not compile:void logMessage(std::string message, std::string level = "INFO", std::string tag) { // ...}This doesn’t work because function arguments are matched to parameters from left to right. In a function call like logMessage("Disk full", "urgent"), "urgent" would be passed to level, leaving the third parameter, tag, unmatched. There’s no way to give tag a value while leaving level at its default.
One fix would be to also give a default argument to the last parameter tag:
void logMessage(std::string message, std::string level = "INFO", std::string tag = "general") { // ...}Another would be to reorder the parameters so that tag comes first:
void logMessage(std::string message, std::string tag, std::string level = "INFO") { // ...}Rule 2: Defaults can’t reference other parameters
A parameter can’t refer to another parameter:
// This will not compile:void makeGrid(int width, int height = width) { // ...}If this were allowed, width would need to be evaluated before it’s used as a default value for height. But C++ doesn’t guarantee the order in which a function’s arguments are evaluated.
If you need a default that depends on another argument, compute it inside the function body instead:
void makeGrid(int width, int height = -1) { if (height == -1) { height = width; } // ...}Rule 3: Defaults go in declarations
If you split your code into a declaration and a separate definition (something you’ll do once you start using multiple header files), the default belongs in the declaration only, not in both places:
// declarationvoid logMessage(std::string message, std::string level = "INFO");// definition, later or in a different filevoid logMessage(std::string message, std::string level) { std::cout << "[" << level << "] " << message << "\n";}Repeating the default in the definition is a compile error.
Checkpoint
What does this program print?
void printBadge(std::string name, std::string tier = "Bronze") {
std::cout << name << ": " << tier << "\n";
}
int main() {
printBadge("Aria");
printBadge("Kai", "Gold");
}What does this print?
void printBadge(std::string name, std::string tier = "Bronze", int ranking) {
std::cout << name << ": " << tier << " (#" << ranking << ")\n";
}
int main() {
printBadge("Kai", "Gold", 1);
}What does this program print?
void printBadge(std::string name, std::string tier = "Bronze");
int main() {
printBadge("Aria");
printBadge("Kai", "Gold");
}
void printBadge(std::string name, std::string tier = "Bronze") {
std::cout << name << ": " << tier << "\n";
}Given this attempted declaration, why won’t it compile?
void allocateBuffer(int blockSize, int alignment, long capacity = blockSize) {
// ...
}