cpp

C++ 템플릿 - 제네릭 프로그래밍

임베디드 친구 2024. 12. 20. 20:08
반응형

C++ 템플릿은 제네릭 프로그래밍(generic programming)을 가능하게 하는 강력한 도구입니다. 템플릿을 사용하면 타입에 의존하지 않는 코드를 작성할 수 있어 코드 재사용성과 유연성이 크게 향상됩니다. 이번 포스팅에서는 C++ 템플릿의 기본 개념부터 실용적인 예제까지 다루어 보겠습니다.


1. 템플릿이란 무엇인가?

템플릿은 컴파일 타임에 타입을 정할 수 있는 코드 블록을 의미합니다. 함수나 클래스 정의에서 특정 타입 대신 템플릿 매개변수를 사용하여 다형성을 제공합니다. 이를 통해 다양한 데이터 타입에서 동작하는 일반화된 코드를 작성할 수 있습니다.


2. 함수 템플릿

함수 템플릿은 함수 정의에서 특정 데이터 타입을 일반화합니다. 가장 간단한 템플릿 형태는 다음과 같습니다:

#include <iostream>

// 함수 템플릿 정의
template <typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << "정수 덧셈: " << add(3, 4) << std::endl;
    std::cout << "실수 덧셈: " << add(3.5, 4.5) << std::endl;
    return 0;
}

출력 결과

정수 덧셈: 7
실수 덧셈: 8

주요 특징

  • template <typename T>는 템플릿을 선언하는 부분입니다.
  • T는 사용자가 제공한 타입으로 대체됩니다.
  • 컴파일러가 함수 호출 시점에 적절한 타입을 유추하여 템플릿을 인스턴스화합니다.

3. 클래스 템플릿

클래스 템플릿은 템플릿 매개변수를 사용하여 특정 타입에 의존하지 않는 클래스 정의를 작성할 수 있습니다.

예제: 클래스 템플릿을 이용한 스택 구현

#include <iostream>
#include <vector>

// 클래스 템플릿 정의
template <typename T>
class Stack {
private:
    std::vector<T> elements;

public:
    void push(T element) {
        elements.push_back(element);
    }

    void pop() {
        if (!elements.empty()) {
            elements.pop_back();
        }
    }

    T top() const {
        if (!elements.empty()) {
            return elements.back();
        }
        throw std::out_of_range("Stack is empty");
    }

    bool isEmpty() const {
        return elements.empty();
    }
};

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    std::cout << "Top of intStack: " << intStack.top() << std::endl;
    intStack.pop();
    std::cout << "Top of intStack after pop: " << intStack.top() << std::endl;

    Stack<std::string> stringStack;
    stringStack.push("Hello");
    stringStack.push("World");
    std::cout << "Top of stringStack: " << stringStack.top() << std::endl;
    return 0;
}

출력 결과

Top of intStack: 20
Top of intStack after pop: 10
Top of stringStack: World

주요 특징

  • 클래스 템플릿은 template <typename T>로 선언합니다.
  • 특정 타입에 독립적인 일반화된 데이터 구조를 만들 수 있습니다.

4. 비타입(non-type) 템플릿 매개변수

C++ 템플릿은 타입뿐만 아니라 정수, 포인터 등 비타입 매개변수도 지원합니다.

예제: 배열 크기를 고정한 템플릿 클래스

#include <iostream>

// 비타입 템플릿 매개변수 사용
template <typename T, int Size>
class Array {
private:
    T data[Size];

public:
    T& operator[](int index) {
        return data[index];
    }

    int size() const {
        return Size;
    }
};

int main() {
    Array<int, 5> intArray;
    for (int i = 0; i < intArray.size(); ++i) {
        intArray[i] = i * 10;
    }

    for (int i = 0; i < intArray.size(); ++i) {
        std::cout << intArray[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}

출력 결과

0 10 20 30 40

주요 특징

  • 비타입 매개변수는 템플릿 정의에 고정된 값을 전달받을 수 있습니다.
  • 배열과 같이 크기가 정해진 데이터 구조를 구현할 때 유용합니다.

5. 템플릿 특수화

템플릿 특수화는 특정 타입에 대해 템플릿의 동작을 맞춤화할 수 있게 합니다.

예제: 템플릿 특수화

#include <iostream>

// 기본 템플릿
template <typename T>
class Printer {
public:
    void print(T value) {
        std::cout << "일반 값: " << value << std::endl;
    }
};

// 템플릿 특수화: 문자열 타입에 대해 맞춤화
template <>
class Printer<std::string> {
public:
    void print(std::string value) {
        std::cout << "문자열 값: " << value << std::endl;
    }
};

int main() {
    Printer<int> intPrinter;
    intPrinter.print(42);

    Printer<std::string> stringPrinter;
    stringPrinter.print("Hello Templates!");
    return 0;
}

출력 결과

일반 값: 42
문자열 값: Hello Templates!

주요 특징

  • 특수화된 템플릿은 template<> 구문을 사용합니다.
  • 특정 타입에 대해 기본 템플릿 동작을 맞춤화할 수 있습니다.

6. 템플릿의 한계와 주의점

  1. 컴파일 시간 증가: 템플릿은 컴파일 타임에 인스턴스화되므로 컴파일 시간이 늘어날 수 있습니다.
  2. 디버깅 어려움: 템플릿 오류는 디버깅이 어렵고 복잡한 컴파일러 메시지를 생성할 수 있습니다.
  3. 코드 크기 증가: 사용된 타입별로 인스턴스화되기 때문에 코드 크기가 증가할 수 있습니다.

7. 결론

템플릿은 C++의 핵심 기능 중 하나로, 코드의 재사용성과 유연성을 극대화합니다. 함수 템플릿, 클래스 템플릿, 비타입 매개변수, 템플릿 특수화 등 다양한 템플릿 기법을 활용하면 더 나은 코드 설계가 가능합니다. 템플릿을 익히고 실무에서 활용해 보세요!

반응형

'cpp' 카테고리의 다른 글

C++ 예외 처리 (Exception Handling)  (0) 2024.12.20
C++ STL(Standard Template Library)  (0) 2024.12.20
C++ 네임스페이스(namespace)  (0) 2024.12.20
C++ 연산자 오버로딩 (Operator Overloading)  (0) 2024.12.19
C++ 상속과 다형성  (0) 2024.12.19