icon안동민 개발노트

함수 템플릿


함수 템플릿의 개념

 함수 템플릿은 C++에서 제공하는 강력한 기능으로, 다양한 데이터 타입에 대해 동작하는 일반화된 함수를 작성할 수 있게 해줍니다. 이를 통해 코드의 재사용성을 높이고, 타입 안전성을 보장할 수 있습니다.

 함수 템플릿의 기본 구조

template <typename T>
T functionName(T parameter) {
    // 함수 본문
}
  • template : 템플릿 선언을 시작하는 키워드입니다.
  • typename T : 템플릿 매개변수를 선언합니다. class T로 쓸 수도 있습니다.
  • T : 템플릿 매개변수로, 실제 사용 시 구체적인 타입으로 대체됩니다.

간단한 함수 템플릿 예제

 다음은 두 값 중 큰 값을 반환하는 함수 템플릿의 예입니다.

#include <iostream>
 
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}
 
int main() {
    std::cout << "Max of 3 and 7: " << max(3, 7) << std::endl;
    std::cout << "Max of 3.14 and 2.72: " << max(3.14, 2.72) << std::endl;
    std::cout << "Max of 'a' and 'z': " << max('a', 'z') << std::endl;
 
    return 0;
}

 이 예제에서 max 함수 템플릿은 정수, 부동소수점, 문자 등 다양한 타입에 대해 동작합니다.

템플릿 인스턴스화

 컴파일러는 함수 템플릿이 호출될 때 실제 인자의 타입을 바탕으로 구체적인 함수를 생성합니다. 이 과정을 템플릿 인스턴스화라고 합니다.

int maxInt = max(3, 7);           // T를 int로 인스턴스화
double maxDouble = max(3.14, 2.72); // T를 double로 인스턴스화
char maxChar = max('a', 'z');      // T를 char로 인스턴스화

 컴파일러는 위의 호출에 대해 다음과 같은 함수들을 생성합니다.

int max(int a, int b) {
    return (a > b) ? a : b;
}
 
double max(double a, double b) {
    return (a > b) ? a : b;
}
 
char max(char a, char b) {
    return (a > b) ? a : b;
}

다중 템플릿 매개변수

 함수 템플릿은 여러 개의 템플릿 매개변수를 가질 수 있습니다.

#include <iostream>
 
template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}
 
int main() {
    std::cout << "3 + 4.5 = " << add(3, 4.5) << std::endl;
    std::cout << "2.7 + 8 = " << add(2.7, 8) << std::endl;
    return 0;
}

 이 예제에서 add 함수는 서로 다른 타입의 두 인자를 받아 더할 수 있습니다. decltype과 후행 반환 타입 구문을 사용하여 반환 타입을 자동으로 추론합니다.

명시적 템플릿 인자 지정

 때로는 컴파일러가 템플릿 인자를 추론하지 못하거나, 특정 타입을 강제하고 싶을 때 명시적으로 템플릿 인자를 지정할 수 있습니다.

#include <iostream>
 
template <typename T>
T square(T x) {
    return x * x;
}
 
int main() {
    auto result1 = square(5);     // T는 int로 추론됨
    auto result2 = square<double>(5);  // T를 명시적으로 double로 지정
 
    std::cout << "Square of 5 as int: " << result1 << std::endl;
    std::cout << "Square of 5 as double: " << result2 << std::endl;
 
    return 0;
}

템플릿 특수화

 특정 타입에 대해 다른 동작을 하는 함수를 정의하고 싶을 때 템플릿 특수화를 사용할 수 있습니다.

#include <iostream>
#include <cstring>
 
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}
 
// char* 타입에 대한 특수화
template <>
const char* max<const char*>(const char* a, const char* b) {
    return (strcmp(a, b) > 0) ? a : b;
}
 
int main() {
    std::cout << "Max of 3 and 7: " << max(3, 7) << std::endl;
    std::cout << "Max of 'hello' and 'world': " << max("hello", "world") << std::endl;
    return 0;
}

 이 예제에서 const char* 타입에 대해 특수화된 max 함수는 문자열 비교를 위해 strcmp를 사용합니다.

함수 템플릿과 오버로딩

 함수 템플릿은 일반 함수와 함께 오버로드될 수 있습니다. 컴파일러는 가장 적합한 함수를 선택합니다.

#include <iostream>
#include <string>
 
// 일반 함수
void print(int x) {
    std::cout << "Printing int: " << x << std::endl;
}
 
// 함수 템플릿
template <typename T>
void print(T x) {
    std::cout << "Printing generic type: " << x << std::endl;
}
 
int main() {
    print(5);        // 일반 함수 호출
    print(3.14);     // 템플릿 함수 호출
    print("Hello");  // 템플릿 함수 호출
    return 0;
}

함수 템플릿의 장단점

 장점

  1. 코드 재사용성 증가
  2. 타입 안전성 보장
  3. 일반화된 알고리즘 구현 가능

 단점

  1. 컴파일 시간 증가
  2. 오류 메시지가 복잡해질 수 있음
  3. 코드 크기 증가 가능성 (코드 블로트)

실습 : 범용 스왑 함수 구현

 다음 요구사항을 만족하는 swap 함수 템플릿을 구현해보세요.

  1. 두 변수의 값을 교환하는 swap 함수 템플릿 작성
  2. 정수, 부동소수점, 문자열 등 다양한 타입에 대해 테스트
  3. 포인터 타입에 대한 특수화 구현 (포인터가 가리키는 값을 교환)
#include <iostream>
#include <string>
 
// 일반적인 swap 함수 템플릿
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
 
// 포인터 타입에 대한 특수화
template <typename T>
void swap(T*& a, T*& b) {
    T* temp = a;
    a = b;
    b = temp;
}
 
// 테스트를 위한 출력 함수 템플릿
template <typename T>
void printPair(const std::string& name, const T& a, const T& b) {
    std::cout << name << ": a = " << a << ", b = " << b << std::endl;
}
 
int main() {
    int a = 5, b = 10;
    printPair("Before swap (int)", a, b);
    swap(a, b);
    printPair("After swap (int)", a, b);
 
    double x = 3.14, y = 2.72;
    printPair("Before swap (double)", x, y);
    swap(x, y);
    printPair("After swap (double)", x, y);
 
    std::string s1 = "Hello", s2 = "World";
    printPair("Before swap (string)", s1, s2);
    swap(s1, s2);
    printPair("After swap (string)", s1, s2);
 
    int* p1 = &a, *p2 = &b;
    printPair("Before swap (pointer)", *p1, *p2);
    swap(p1, p2);
    printPair("After swap (pointer)", *p1, *p2);
 
    return 0;
}

연습 문제

  1. 두 개의 값을 입력받아 그 중 작은 값을 반환하는 min 함수 템플릿을 작성하세요.
  2. 세 개의 값을 입력받아 그 중 가장 큰 값을 반환하는 max3 함수 템플릿을 작성하세요.
  3. 배열의 모든 원소의 합을 계산하는 sum 함수 템플릿을 작성하세요. (힌트 : 함수 매개변수로 배열과 배열의 크기를 받으세요)


참고 자료

  • "C++ Templates : The Complete Guide" by David Vandevoorde and Nicolai M. Josuttis
  • "Effective C++" by Scott Meyers (항목 41 : 템플릿 프로그래밍에서 암시적 인터페이스와 컴파일 타임 다형성을 이해하자)
  • "Modern C++ Design" by Andrei Alexandrescu
  • C++ Reference: Function templates
  • C++ Core Guidelines : T : Templates and generic programming