템플릿 (C++)
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
C++ 템플릿은 함수, 클래스, 변수에 적용되어 다양한 데이터 형식에 대해 일반화된 코드를 작성할 수 있게 해주는 기능이다. C++11부터 가변 템플릿을 지원하며, 함수 템플릿은 여러 타입의 인자를 받아 함수처럼 동작하고, 클래스 템플릿은 범용 컨테이너를 만드는 데 사용된다. 변수 템플릿은 C++14부터 지원하며, 템플릿 특수화를 통해 특정 타입에 대한 맞춤형 구현을 제공할 수 있다. 템플릿 메타프로그래밍을 통해 컴파일 시간에 코드 생성이 가능하지만, 컴파일러 지원, 오류 메시지, 코드 비대화 등의 단점도 존재한다. 다른 언어의 제네릭과 비교하여 C++ 템플릿은 더 강력한 기능을 제공하며, D 언어의 템플릿은 C++ 템플릿보다 개선된 기능을 제공한다.
더 읽어볼만한 페이지
- 메타프로그래밍 - 위생 매크로
위생 매크로는 변수 섀도잉 문제를 해결하여 매크로 확장의 안전성을 보장하며, Scheme과 같은 언어에서 식별자의 어휘적 범위를 보존하고 우발적인 포착을 방지한다. - 메타프로그래밍 - 템플릿 메타프로그래밍
템플릿 메타프로그래밍은 템플릿을 활용하여 컴파일 시간에 계산을 수행하는 프로그래밍 기법으로, 코드 중복을 줄이고 런타임 성능을 향상하며 정적 다형성을 구현하는 데 사용된다. - 제네릭 프로그래밍 - 다형성 (컴퓨터 과학)
다형성은 프로그래밍 언어에서 이름이 여러 자료형에 사용되거나 객체가 여러 타입으로 취급되는 능력으로, 코드 재사용성과 유연성을 높이며 임시 다형성, 매개변수 다형성, 서브타이핑 등으로 분류되고 객체 지향 프로그래밍의 중요한 특징이며 정적, 동적 다형성으로 구현된다. - 제네릭 프로그래밍 - 표준 템플릿 라이브러리
표준 템플릿 라이브러리(STL)는 알렉산더 스테파노프의 주도로 탄생한 C++ 라이브러리로서, 제네릭 프로그래밍을 지원하기 위해 컨테이너, 반복자, 알고리즘, 함수 객체 등의 핵심 구성 요소로 이루어져 있으며, HP에서 무료로 공개한 구현을 기반으로 다양한 구현체가 존재한다. - C++ - 헤더 파일
헤더 파일은 프로그래밍 언어에서 코드 재사용성, 모듈화, 컴파일 시간 단축에 기여하며 함수 프로토타입, 변수 선언 등을 포함하고 `#include` 지시어로 소스 코드에 포함되어 사용되는 파일이다. - C++ - 소멸자 (컴퓨터 프로그래밍)
소멸자는 객체가 메모리에서 제거되기 직전에 호출되는 멤버 함수로, 객체 자원 해제 및 정리 작업을 수행하며, C++ 등 여러 언어에서 구현되고 메모리 누수 방지에 기여한다.
템플릿 (C++) | |
---|---|
설명 | |
언어 | C++ |
개요 | |
유형 | 일반화 프로그래밍 |
역사 | |
최초 출시 | 1990년경 |
설계자 | 비야네 스트롭스트룹 |
특징 | |
특징 | 함수나 클래스를 작성할 때, 자료형을 지정하는 대신 타입 매개변수를 사용하여 작성할 수 있다. |
타입 매개변수 | 템플릿을 사용할 때 특정 타입으로 구체화된다. |
컴파일 타임 | 템플릿은 컴파일 타임에 구체화되므로, 실행 시간 오버헤드가 거의 없다. |
사용 예시 | |
함수 템플릿 | 두 변수의 값을 교환하는 함수를 템플릿으로 작성할 수 있다. |
클래스 템플릿 | 다양한 자료형을 저장할 수 있는 배열 클래스를 템플릿으로 작성할 수 있다. |
장점 | |
코드 재사용성 | 템플릿을 사용하면 여러 자료형에 대해 동일한 코드를 재사용할 수 있다. |
타입 안전성 | 템플릿은 컴파일 타임에 타입 검사를 수행하므로, 타입 관련 오류를 방지할 수 있다. |
성능 | 템플릿은 컴파일 타임에 구체화되므로, 실행 시간 오버헤드가 거의 없다. |
단점 | |
코드 부풀림 | 템플릿을 사용할 때마다 코드가 생성되므로, 실행 파일의 크기가 커질 수 있다. |
컴파일 시간 증가 | 템플릿은 컴파일 타임에 구체화되므로, 컴파일 시간이 증가할 수 있다. |
복잡한 구문 | 템플릿 구문이 복잡하여 사용하기 어려울 수 있다. |
2. 템플릿의 종류
C++ 템플릿은 크게 함수 템플릿, 클래스 템플릿, 변수 템플릿으로 나눌 수 있다.
종류 | 설명 | 도입 버전 |
---|---|---|
함수 템플릿 | 함수처럼 동작하며, 다양한 타입의 인수를 가질 수 있다. | C++98 |
클래스 템플릿 | 매개변수를 기반으로 클래스를 생성하며, 주로 컨테이너 구현에 사용된다. | C++98 |
변수 템플릿 | 변수에 템플릿을 적용할 수 있다. | C++14 |
C++11부터는 가변 템플릿을 통해 가변 개수의 인수를 사용할 수 있게 되었다. 이전 버전의 C++에서는 템플릿이 항상 가변이 아니었다.[2]
2. 1. 함수 템플릿
함수 템플릿은 여러 다른 자료형(int, long, float, double, class 등)에 대해 같은 역할을 하는 함수를 생성하는 기능이다. 템플릿은 `template예를 들어, C++ 표준 라이브러리에는 두 값 중 큰 값을 반환하는 `max(x, y)` 함수 템플릿이 있다. 이 함수는 다음과 같이 정의할 수 있다.
```cpp
template
Type max(Type a, Type b) {
return a > b ? a : b;
}
```
이 함수 템플릿은 `a > b` 연산이 가능한 모든 자료형에 대해 동작한다. 만약 템플릿을 사용하지 않고 오버로딩으로 구현한다면, 각 자료형에 대해 별도의 함수를 정의해야 한다.
컴파일러는 함수 호출 시 사용된 자료형을 기반으로 템플릿을 인스턴스화(실체화)한다. 예를 들어:
```cpp
#include
int main() {
// max
std::cout << max(3, 7) << std::endl;
// max
std::cout << max(3.0, 7.0) << std::endl;
// max
std::cout << max
return 0;
}
```
처음 두 경우는 컴파일러가 자료형을 `int`와 `double`로 추론하지만, 세 번째 경우는 두 인자의 자료형이 다르므로 명시적으로 `max
함수 템플릿은 `y < x` 표현식이 유효하고, 사용자 정의 타입의 경우 작음 연산자(`<`)가 오버로드되어 있으면 인스턴스화할 수 있다.
C++20부터는 약식 함수 템플릿을 통해 더 간결하게 템플릿 함수를 정의할 수 있다. 함수 선언의 매개변수에 `auto` 또는 개념 `auto`를 사용하면 된다.[4]
```cpp
void f1(auto); // template
void f2(C1 auto); // C1이 개념인 경우 template
2. 2. 클래스 템플릿
클래스 템플릿은 템플릿 매개변수에 따라 클래스를 생성하는 기능이다. 주로 컨테이너 구현에 사용된다.[5] C++ 표준 라이브러리에는 표준 템플릿 라이브러리에서 가져온 vector와 같은 많은 클래스 템플릿이 있다.클래스 템플릿은 템플릿 인수로 주어진 일련의 타입을 전달하여 인스턴스화된다.[5] 예를 들어, 표준 라이브러리의 고정 크기 배열 타입인 `std::array`는 배열이 저장하는 객체의 타입을 나타내는 타입과, 배열이 저장하는 요소의 개수를 나타내는 `std::size_t` 타입의 숫자 모두에 대해 템플릿화된다.
```cpp
template
```
그리고 여섯 개의 `char`로 이루어진 배열은 다음과 같이 선언할 수 있다.
```cpp
array
```
C++11 표준까지 템플릿은 '''클래스 템플릿'''과 함수 템플릿으로 나눌 수 있었고, C++14에서는 '''변수 템플릿'''도 지원하게 되었다.
STL에는 링크 리스트로 `std::list`가 있다. 정수 리스트를 만들려면 `std::list
C++11에서는 가변 인자 템플릿 (variadic template)이 지원된다. 가변 길이 인수의 함수나 매크로처럼 `...`이라는 표기를 사용하여 기술한다.
2. 3. 변수 템플릿 (C++14)
C++14에서는 변수에 템플릿을 사용할 수 있다. 템플릿 매개변수에 따라 값을 정의할 수 있는데, 다음은 그 사용 예시이다.```cpp
template
constexpr T pi = T{3.141592653589793238462643383L}; // (거의) std::numbers::pi에서 가져옴
2. 4. 가변 템플릿 (C++11)
C++11에서는 가변 템플릿을 통해 가변 인자 함수와 유사하게 가변 개수의 인수를 사용할 수 있게 되었다. 가변 길이 인수 함수나 매크로처럼 `...` 표기법을 사용하여 나타낸다.[7]3. 템플릿 특수화
템플릿에서 함수나 클래스가 인스턴스화될 때, 사용된 인자 집합에 대해 해당 템플릿의 특수화가 컴파일러에 의해 생성되며, 이 특수화를 생성된 특수화라고 한다.
- 클래스 템플릿이 매개변수의 하위 집합으로 특수화되면 부분 템플릿 특수화라고 한다(함수 템플릿은 부분적으로 특수화될 수 없다).
- 모든 매개변수가 특수화되면 ''전체 특수화''이다.
3. 1. 명시적 특수화
프로그래머는 주어진 템플릿 타입 인자 집합에 대해 함수(또는 클래스)의 특수 버전을 구현할 수 있는데, 이를 명시적 특수화라고 한다. 이 방식을 통해 특정 템플릿 타입은 해당 타입에 최적화되거나 일반적인 구현보다 더 의미있는 구현을 가질 수 있다.명시적 특수화는 템플릿 매개변수의 특정 선택에 대한 함수 또는 클래스의 동작이 일반적인 동작, 즉 주 템플릿 또는 템플릿에 의해 생성된 코드와 달라야 할 때 사용된다. 예를 들어, 다음 템플릿 정의는 `const char*` 타입의 인자에 대한 `max()`의 특정 구현을 정의한다.
```cpp
#include
template<>
const char* max(const char* a, const char* b) {
// 일반적으로, 두 C 문자열 간의 직접 비교 결과는 정의되지 않은 동작입니다.
// std::strcmp를 사용하면 정의됩니다.
return std::strcmp(a, b) > 0 ? a : b;
}
```
템플릿의 명시적 특수화는 특정 형식에 최적화할 수 있도록 하는 것과 코드 비대화 감소에 기여한다는 두 가지 목적이 있다.
예를 들어 `sort()` 함수 템플릿에 대해 생각해보자. 이러한 함수의 동작은 첫째로 컨테이너의 특정 위치의 두 값을 교체하거나 바꾸는 것이다. 값이 많은 경우 (각 요소를 저장하기 위해 메모리를 소비한다는 점에서) 객체에 대한 포인터 목록을 먼저 구축하고 해당 포인터를 정렬하여 최종 정렬된 시퀀스를 구축하는 것이 많은 경우 빠르다. 값이 매우 적으면 단순히 필요에 따라 값을 스왑으로 교체하는 것이 가장 빠르다. 그러나 파라미터화된 형식이 포인터 형인 경우 포인터 배열을 구축할 필요가 없다. 템플릿의 명시적 특수화는 템플릿 구현자가 여러 개의 다른 구현을 작성할 수 있게 하며, 파라미터화된 형식이 각 구현에서 사용되어야 하는 특징을 지정할 수 있게 한다.
3. 2. 부분 특수화
템플릿 부분 특수화는 템플릿 가인수의 일부를 특수화하는 것이다. 명시적 특수화와는 달리, 특수화되지 않고 추상화된 채로 남아있는 템플릿 가인수가 존재한다.4. 템플릿 메타프로그래밍
C++ 템플릿은 실행 시점이 아닌 컴파일 시점에 코드의 일부를 사전 평가하는 방법인 템플릿 메타프로그래밍에도 활용될 수 있다. C++ 템플릿은 튜링 완전하다.[8] 즉, 템플릿을 이용하여 컴파일 시간에 복잡한 연산을 수행할 수 있다.
예를 들어, 다음과 같이 템플릿을 사용하여 팩토리얼을 컴파일 시간에 계산할 수 있다.
```cpp
template
struct Factorial {
static const int value = N * Factorial
};
template <>
struct Factorial<0> {
static const int value = 1;
};
```
위 코드에서 `Factorial
하지만 컴파일러에 의한 제한이 있다. 표준 상으로는 재귀의 깊이는 참고 사항이며 처리계 한계에 해당한다. C++ 작업 초안 중 하나인 N3797에서는 1024를 최소값으로 예시하고 있다.[8] 재귀 제한이 없다면 컴파일이 영원히 끝나지 않는 코드를 작성할 수 있으며, 컴파일러는 이를 판단할 수 없기 때문이다. (정지 문제 참조)
5. 템플릿의 장단점
템플릿은 코드 재사용성, 타입 안정성, 실행 시간 효율성을 제공한다.[3] 하지만 몇 가지 단점도 존재한다.
'''함수 템플릿'''은 다양한 유형의 인수를 가질 수 있다는 점을 제외하고 함수처럼 동작한다. 함수 템플릿은 일련의 함수를 나타낸다. 형식 매개변수를 사용하여 함수 템플릿을 선언하는 형식은 다음과 같다.
```cpp
template
template
```
두 표현식은 동일한 의미를 가지며 정확히 동일한 방식으로 동작한다.
C++의 템플릿은 컴파일 시점에 타입 안전하며, C 언어의 전처리기 매크로로 대체할 수 있는 용법이 있다. 예를 들어, `max` 함수 템플릿 대신 다음과 같은 `MAX` 매크로를 정의할 수 있다.
```c
#define MAX(a, b) ((a) < (b) ? (b) : (a))
```
매크로와 템플릿은 모두 컴파일 시점까지 전개된다. 매크로는 항상 인라인 전개되지만, 함수 템플릿은 컴파일러의 판단에 따라 인라인 함수로 취급된다. 따라서 (함수 템플릿이 인라인 전개되는 한) 실행 시의 오버헤드는 양쪽 모두 발생하지 않는다.
그러나 템플릿은 매크로에 비해 타입 안전하다는 점에서 큰 차이가 있다. 템플릿에서는 함수 형식 매크로에서 자주 발생하는 오류나 인수의 의도하지 않은 다중 평가가 일어나지 않는다.
템플릿에는 크게 세 가지 단점이 있다. 그것은 컴파일러 지원, 빈약한 오류 메시지, 코드 비대화이다.
과거에는 많은 컴파일러의 템플릿 지원이 부족하여 템플릿을 사용하면 이식성이 저하될 우려가 있었다. 하지만 C++98 이후에는 템플릿을 포함한 규격 표준화와 컴파일러 대응이 진행되어 개선되었다.
거의 모든 컴파일러는 템플릿을 사용한 코드에서 오류를 감지했을 때, 혼란을 유발하는 길고 쓸모없는 오류 메시지를 출력한다.
템플릿은 실체화될 때마다 코드가 생성되므로 무질서하게 사용하면 코드 팽창을 일으켜 실행 파일이 커지는 문제가 있다. 그러나 일부 경우에는 템플릿의 특수화를 잘 활용함으로써 그러한 코드의 비대화를 극적으로 줄일 수 있다.
5. 1. 장점
템플릿은 여러 자료형(int, long, float, double, class 등)에 대해 동일한 알고리즘을 적용할 수 있게 해주어 코드 재사용성을 높인다. 예를 들어, C++ 표준 라이브러리(STL)의 `max(x, y)` 함수는 두 값 중 큰 값을 반환하는데, 템플릿을 사용하여 다양한 자료형에 대해 동작하도록 구현되어 있다.[3] 만약 템플릿이 없다면 각 자료형에 대해 별도의 `max` 함수를 오버로딩하여 정의해야 했을 것이다.
```cpp
template
Type max(Type a, Type b) {
return a > b ? a : b;
}
```
템플릿은 컴파일 시간에 타입 검사를 수행하여 런타임 오류를 줄여 타입 안정성을 높인다. 또한, 컴파일 시간에 코드가 생성되므로 런타임 오버헤드가 적어 실행 시간 효율성이 높다.
템플릿은 상속 관계 없이 필요한 연산자(예: `<` 연산자)만 정의하면 템플릿을 사용할 수 있는 정적 덕 타이핑을 지원한다. 즉, 특정 인터페이스를 상속받지 않아도, 필요한 연산자만 오버로딩되어 있으면 템플릿을 사용할 수 있다.
5. 2. 단점
템플릿은 C++에서 강력한 기능이지만, 몇 가지 단점도 존재한다.
C++03에는 `export` 키워드에 의한 템플릿의 익스포트 기능이 존재했지만, C++11에서는 삭제되었다.
6. 다른 언어와의 비교
자바와 C# 1.0 같은 일부 언어는 초기에는 템플릿 개념을 포함하지 않았다. 그러나 이후 자바는 제네릭을 채택하여 템플릿의 동작을 모방했지만 기술적으로는 다르다. C#은 .NET 2.0에서 제네릭(매개변수화된 유형)을 추가했다. Ada의 제네릭은 C++ 템플릿보다 먼저 나왔다.[6]
C++ 템플릿, 자바 제네릭, .NET 제네릭은 유사하다고 여겨지지만, 제네릭은 C++ 템플릿의 기본적인 동작만을 모방한다.[6] Boost 및 STLSoft와 같은 라이브러리와 STL 구현에서 사용되는 템플릿 메타 프로그래밍(명시적 또는 부분 특수화, 기본 템플릿 인수, 템플릿 비타입 인수, 템플릿 템플릿 인수 등)에 사용되는 일부 고급 템플릿 기능은 제네릭에서는 사용할 수 없다.
C++ 템플릿에서 컴파일 시간 처리는 역사적으로 템플릿 인수에 대한 패턴 매칭으로 수행되었다. 예를 들어, 팩토리얼 예제에서 템플릿 기본 클래스는 부등식 검사 없이 0을 매칭하여 구현된다. 그러나 C++11에서 `std::conditional`과 같은 표준 라이브러리 기능이 도입되면서 조건부 템플릿 인스턴스화를 처리하는 더 유연한 방법이 제공되었다.
D 언어에도 템플릿이 존재하지만, C++의 템플릿보다 더 개선되었다. 예를 들어 템플릿 인수로 문자열이나 부동 소수점, 변수나 함수 등의 심볼을 전달할 수 있다. 이를 통해 고차 함수의 템플릿 인수에 문자열로 전달된 식을 인라인화하는 등의 기법이 가능해졌다. 그 외에도 템플릿 제약을 지정할 수 있는 내장 구문이나, 템플릿 인수에 의해 실체화되는 코드를 변화시킬 수 있는 `static if` 등의 기능이 있으며, 일부는 D 언어 개발 멤버 등에 의해 C++로의 도입이 제안되고 있다.
Ada는 C++가 템플릿을 채용하기 이전부터 제네릭(범용체)을 가지고 있었지만, 이는 템플릿의 특수화를 수행할 수 없는 등 C++의 템플릿에 비해 기능이 제한적이었으며, C++와 같은 템플릿 메타프로그래밍을 수행할 수 없었다.
참조
[1]
웹사이트
The C++ Programming Language
http://www.stroustru[...]
2004-09-08
[2]
웹사이트
https://web.archive.[...]
[3]
웹사이트
Why C++ Supports both Class and Typename for Type Parameters
https://docs.microso[...]
2004-08-11
[4]
웹사이트
P1141R1 - Yet another approach for constrained declarations
http://open-std.org/[...]
2018-11-11
[5]
서적
C++ Templates: The Complete Guide
Addison Wesley
[6]
웹사이트
Differences Between C++ Templates and C# Generics (C# Programming Guide)
http://msdn.microsof[...]
2024-03-12
[7]
웹사이트
std::list - cppreference.com
https://ja.cpprefere[...]
[8]
문서
Annex B (informative) Implementation quantities
http://www.open-std.[...]
[9]
웹인용
The C++ Programming Language (Third Edition and Special Edition)
http://www2.research[...]
2004-09-08
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com