콘텐츠로 이동

함수 오버로딩 (Function Overloading)

오버로드된 함수(생성자 포함)는, 호출 지점을 보는 독자가 어떤 오버로드가 호출되는지 먼저 알아내지 않고도 무슨 일이 일어나는지 잘 파악할 수 있을 때만 사용하세요.

정의:

const std::string&을 받는 함수를 작성하고, const char*를 받는 다른 함수로 그것을 오버로드할 수 있습니다. 다만 이런 경우에는 std::string_view를 대신 고려해 보세요.

class MyClass {
 public:
  void Analyze(const std::string& text);
  void Analyze(const char* text, size_t textlen);
};

장점:

오버로딩은 이름이 같은 함수가 서로 다른 인자를 받을 수 있게 해서 코드를 더 직관적으로 만들 수 있습니다. 템플릿화된 코드에서는 필요할 수도 있고, 방문자(Visitor) 패턴에서 편리할 수도 있습니다.

const나 참조 한정에 따른 오버로딩은 유틸리티 코드를 더 쓰기 좋게, 또는 더 효율적으로, 또는 둘 다로 만들어 줄 수 있습니다. 자세한 내용은 TotW #148을 참조하세요.

단점:

함수가 인자 타입만으로 오버로드되어 있으면, 독자는 무슨 일이 일어나는지 알기 위해 C++의 복잡한 매칭 규칙을 이해해야 할 수도 있습니다. 또한 파생 클래스가 어떤 함수의 변형 중 일부만 재정의하면 상속의 의미론 때문에 혼란스러워하는 사람이 많습니다.

결정:

변형들 사이에 의미론적 차이가 없다면 함수를 오버로드해도 됩니다. 이런 오버로드들은 타입이나 한정자, 인자 개수가 다를 수 있습니다. 다만 그런 호출을 읽는 사람이 오버로드 집합의 어느 멤버가 선택되었는지까지 알 필요는 없어야 하며, 그 집합에 속한 무언가가 호출된다는 것만 알면 되어야 합니다.

이렇게 하나로 묶인 설계를 반영하려면, 오버로드 집합 전체를 문서화하는 포괄적인 "우산" 주석 하나를 첫 번째 선언 앞에 두는 편을 선호하세요.

독자가 그 우산 주석을 특정 오버로드와 연결하기 어려울 만한 곳에서는 개별 오버로드에 주석을 달아도 괜찮습니다.


옮긴이 풀이

핵심: 호출 지점에서 "무슨 일이 일어나는지" 분명할 때만 오버로드

오버로드된 함수는, 호출하는 쪽 독자가 어떤 오버로드가 선택되는지 따지지 않고도 무슨 일이 일어나는지 알 수 있을 때만 쓰세요.

class MyClass {
 public:
  void Analyze(const std::string& text);
  void Analyze(const char* text, size_t textlen);
};

위처럼 const std::string&과 const char*로 오버로드할 수 있지만, 이런 경우엔 std::string_view 하나로 받는 편이 더 낫습니다.

좋은 오버로드 / 나쁜 오버로드

  • 좋음: 변형 간 의미 차이가 없어, 어느 게 불려도 결과가 같은 경우. const/참조 한정에 따른 오버로드도 유용합니다.
  • 나쁨: 인자 타입만으로 갈리고, 독자가 C++의 복잡한 오버로드 해결 규칙을 알아야 동작을 이해할 수 있는 경우. 파생 클래스가 일부 변형만 재정의하면 상속 의미가 헷갈립니다.

문서화

하나의 통합된 오버로드 집합은, 첫 선언 앞에 전체를 설명하는 하나의 "우산" 주석을 선호합니다. 독자가 특정 오버로드와 연결하기 어려울 때만 개별 주석을 답니다.