본문으로 건너뛰기 C++ Default Arguments | 'Default Arguments' Guide

C++ Default Arguments | 'Default Arguments' Guide

C++ Default Arguments | 'Default Arguments' Guide

이 글의 핵심

Default arguments allow optional parameters in a single function signature. Organizes declaration/redeclaration rules, pitfalls with overloading/virtual functions, and API design patterns.

What are Default Arguments?

Default arguments allow specifying default values for function parameters, enabling argument omission during calls. Can replace function overloading to make code more concise.

void greet(const std::string& name = "Guest") {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    greet("Alice");  // Hello, Alice!
    greet();         // Hello, Guest!
}

Why needed?:

  • Conciseness: Implement optional parameters without overloading
  • Backward compatibility: Maintain existing code when adding new parameters
  • Readability: Set frequently used values as defaults
// ❌ Overloading: Code duplication
void connect(const std::string& host) {
    connect(host, 8080);
}

void connect(const std::string& host, int port) {
    // Connection logic
}

// ✅ Default argument: Concise
void connect(const std::string& host, int port = 8080) {
    // Connection logic
}

Basic Usage

// Single default argument
void func(int x = 10) {
    std::cout << x << std::endl;
}

// Multiple default arguments
void func(int x = 10, int y = 20, int z = 30) {
    std::cout << x << ", " << y << ", " << z << std::endl;
}

int main() {
    func();           // 10, 20, 30
    func(1);          // 1, 20, 30
    func(1, 2);       // 1, 2, 30
    func(1, 2, 3);    // 1, 2, 3
}

Rules

// ✅ Defaults from right
void func(int x, int y = 20, int z = 30) {
    // OK
}

// ❌ Gap in middle
// void func(int x, int y = 20, int z) {  // Error
//     // ...
// }

// ✅ All defaults
void func(int x = 10, int y = 20, int z = 30) {
    // OK
}

Default Argument Rules in Detail:

  1. Continuous from right: Parameters with default values must be continuous from right.
// ✅ Correct examples
void f1(int a, int b = 2, int c = 3);
void f2(int a = 1, int b = 2, int c = 3);

// ❌ Incorrect examples
// void f3(int a = 1, int b, int c = 3);  // Error: b has no default in middle
// void f4(int a = 1, int b, int c);      // Error: only a has default
  1. Defaults only in declaration: Specify default values only in header file declaration, omit in implementation file.
// header.h
void func(int x, int y = 20);

// source.cpp
void func(int x, int y) {  // No default value
    std::cout << x << ", " << y << '\n';
}
  1. Can add defaults in redeclaration: Can add default values in redeclaration for same parameter, but duplicate specification not allowed.
void func(int x, int y, int z);  // Declaration 1

void func(int x, int y, int z = 30);  // Declaration 2: Add default to z

void func(int x, int y = 20, int z);  // Declaration 3: Add default to y

// Final: func(int x, int y = 20, int z = 30)

// ❌ Duplicate specification
// void func(int x, int y = 20, int z = 30);  // Error: y, z duplicate

Constraints and Pitfalls with Virtual Functions

Default arguments are not virtual. Even in virtual function calls, which default value is used is determined at compile-time by the static type used in the call.

// Type definition
struct Base {
    virtual void f(int x = 1) { std::cout << "B " << x << '\n'; }
};
struct Derived : Base {
    void f(int x = 2) override { std::cout << "D " << x << '\n'; }
};

int main() {
    Derived d;
    Base& b = d;
    d.f();   // D 2  — Static type Derived → Derived's default
    b.f();   // D 1  — Static type Base → Base's default (not derived's!)
}

Having different default values in override function in derived class is syntactically possible, but only confuses users calling through base pointer. In public API, safer to put default values only in base declaration and omit in derived, or handle defaults with non-virtual wrapper.

Also, when overriding virtual function with default arguments, signature (name·parameter list) must match, so default value list must align with header design separate from inheritance rules.

Practical Examples

Example 1: Log Function

#include <iostream>
#include <fstream>
#include <string>

enum class LogLevel {
    INFO,
    WARNING,
    ERROR
};

void log(const std::string& message, 
         LogLevel level = LogLevel::INFO,
         const std::string& file = "log.txt") {
    std::ofstream ofs(file, std::ios::app);
    
    switch (level) {
        case LogLevel::INFO:
            ofs << "[INFO] ";
            break;
        case LogLevel::WARNING:
            ofs << "[WARNING] ";
            break;
        case LogLevel::ERROR:
            ofs << "[ERROR] ";
            break;
    }
    
    ofs << message << '\n';
}

int main() {
    log("Application started");  // Default: INFO, log.txt
    log("Low memory", LogLevel::WARNING);  // WARNING, log.txt
    log("Crash", LogLevel::ERROR, "error.txt");  // ERROR, error.txt
}

Summary

Key Points

  1. Default arguments: Specify default values for parameters
  2. Rules: Continuous from right, declaration only
  3. Virtual functions: Defaults determined by static type
  4. Overloading: Can cause ambiguity
  5. API design: Use for 2-3 parameters max

When to Use

Use default arguments when:

  • Need optional parameters
  • Want to maintain backward compatibility
  • Have frequently used default values
  • Simplifying overload set

Don’t use when:

  • Too many parameters (use options struct)
  • Virtual function with different defaults
  • Causes ambiguity with overloads
  • Expression has side effects

Best Practices

  • ✅ Put defaults in header declaration
  • ✅ Keep defaults simple and constant
  • ✅ Limit to 2-3 default parameters
  • ❌ Don’t use different defaults in virtual override
  • ❌ Don’t use expressions with side effects

Master default arguments for cleaner function APIs! 🚀


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Default arguments allow optional parameters in a single function signature.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, default-arguments, function, default-value.