C++ Default Parameter - 6. 특수한 상황에서의 Default Parameter 활용

2025. 3. 27. 15:50프로그래밍 언어/C++

 

✅ 6. 특수한 상황에서의 Default Parameter 활용


📌 1. 템플릿 함수에서 기본 인자 사용

템플릿 함수에서도 일반 함수처럼 기본 인자를 지정할 수 있습니다.
단, 기본값은 컴파일 타임에 평가 가능해야 하며, 타입 유추와의 충돌에 주의해야 합니다.

✅ 함수 매개변수에 기본값 지정

template <typename T>
T multiply(T a, T b = T(1)) {
    return a * b;
}

multiply(5);       // b = 1
multiply(5, 2);    // b = 2

✅ 템플릿 매개변수 자체에 기본값 지정

template <typename T = int>
T identity(T value = 0) {
    return value;
}

identity();         // T = int, value = 0
identity(3.14);     // T = double, value = 3.14
구분 예시 설명
템플릿 인자 template <typename T = int> 타입 자체의 기본값
함수 인자 int func(int x = 10) 값 자체의 기본값

📌 2. CRTP(Curiously Recurring Template Pattern)에서 기본 인자 활용

CRTP는 상위 템플릿 클래스에 하위 클래스 자신을 전달하여 상속 구조를 만드는 패턴입니다.
이 구조에서 기본 인자는 공통 동작의 유연성을 높이는 데 활용됩니다.

✅ 예시

template <typename Derived>
class Base {
public:
    void execute(int repeat = 1) {
        for (int i = 0; i < repeat; ++i) {
            static_cast<Derived*>(this)->run();
        }
    }
};

class Worker : public Base<Worker> {
public:
    void run() {
        std::cout << "Working...\n";
    }
};

Worker w;
w.execute();     // 기본값 repeat = 1
w.execute(3);    // 3회 반복 실행

✅ 기본 인자를 통해 실행 횟수, 로그 수준 등 공통 제어가 가능
✅ 하위 클래스는 핵심 동작만 정의하면 됨 → 코드 재사용에 유리


📌 3. 클래스 멤버 함수에서 기본 인자 지정

클래스의 멤버 함수도 기본 인자를 사용할 수 있으며, 선언부에만 작성하는 것이 원칙입니다.

class Logger {
public:
    void log(std::string msg = "Default message");
};

void Logger::log(std::string msg) {
    std::cout << msg << '\n';
}
  • 선언부와 정의부가 분리될 경우, 정의부에는 기본값을 쓰지 않아야 중복 정의 문제가 발생하지 않습니다.

📌 4. 가상 함수에서 기본 인자 사용 시 주의점

기본 인자는 정적 바인딩, 가상 함수 호출은 동적 바인딩을 따릅니다.
이로 인해 함수는 자식 클래스 것이 호출되더라도, 기본값은 부모 클래스 기준으로 적용됩니다.

❌ 잘못된 예시

class Base {
public:
    virtual void greet(std::string name = "Base") {
        std::cout << "Hello, " << name << '\n';
    }
};

class Derived : public Base {
public:
    void greet(std::string name = "Derived") override {
        std::cout << "Hi, " << name << '\n';
    }
};

Base* obj = new Derived();
obj->greet();  // 출력: Hi, Base ← 기본값은 Base 기준

✅ 해결 방법 ①: 기본 인자 제거 + 내부 처리

class Derived : public Base {
public:
    void greet(std::string name) override {
        if (name.empty()) name = "Derived";
        std::cout << "Hi, " << name << '\n';
    }
};

Derived d;
d.greet("");  // → Hi, Derived

✅ 해결 방법 ②: 비가상 래퍼 함수 사용

class Base {
public:
    void greetWrapper(std::string name = "Base") {
        greet(name);
    }

    virtual void greet(std::string name) {
        std::cout << "Hello, " << name << '\n';
    }
};

✅ 기본 인자는 비가상 함수에만 두고,
✅ 실제 동작은 가상 함수에서 처리 → 의도한 바인딩과 값 적용이 모두 보장


📌 5. static 멤버 함수에서 기본 인자

정적(static) 멤버 함수는 객체 없이 클래스 이름으로 호출되며,
기본 인자 사용에 아무런 제한이 없습니다.

class Settings {
public:
    static void reset(int level = 1) {
        std::cout << "Resetting to level " << level << '\n';
    }
};

Settings::reset();    // 기본값 사용 → 1
Settings::reset(5);   // 명시적 값 전달
  • 가상 함수 아님 → 바인딩 문제 없음
  • 일반 함수와 동일한 방식으로 활용 가능

📌 핵심 요약표

항목 설명
템플릿 인자 vs 함수 인자 타입과 값의 기본값을 구분해서 사용
CRTP와 기본 인자 활용 반복 제어, 옵션 전달 등 공통 로직 구성 가능
클래스 멤버 함수 선언부에만 기본 인자 작성 (정의부 생략)
가상 함수와 기본 인자 기본 인자는 정적 타입 기준 → 동작 혼란 가능성 있음
가상 함수 해결 방법 기본 인자 제거 후 내부 처리 or 비가상 래퍼 함수 사용
static 멤버 함수와 기본 인자 일반 함수처럼 자유롭게 사용 가능