소개
템플릿은 일반 프로그래밍의 기초입니다.
이전에 함수 오버로딩을 도입했습니다 . 동일한 함수 이름과 다른 매개변수 목록을 사용하여 오버로드된 함수를 여러 개 정의하여 다양한 유형의 유사한 작업을 수행할 수 있습니다. 오버로드된 함수를 호출할 때 전달된 매개 변수에 따라 적절한 오버로드된 함수가 호출되므로 이러한 메서드를 호출하는 데 편리합니다.
그러나 함수 오버로딩에는 여전히 몇 가지 단점이 있습니다.
오버로드된 함수는 여전히 자체적으로 정의해야 하며 코드 재사용률이 높지 않습니다. 구현 유형을 추가해야 할 때 오버로드된 함수를 정의해야 하며 오버로드된 함수의 유지 관리 가능성이 높지 않으며 문제가 발생하면 오버로드된 각 함수를 하나씩 수정해야 할 수 있습니다.
일반 프로그래밍은 이 문제를 해결할 수 있습니다.모든 유형에 공통적인 코드만 작성할 수 있습니다.사용이 필요할 때 컴파일러는 해당 코드를 생성하여 코드 재사용의 수단입니다. 템플릿은 일반 프로그래밍의 기초입니다.
C++ 템플릿은 함수 템플릿과 클래스 템플릿으로 나뉩니다.
기능 템플릿
함수 템플릿은 유형에 관계없이 동일한 작업을 수행하는 함수 클래스를 나타냅니다. 함수 템플릿은 사용될 때 컴파일러에 의해 자동으로 인스턴스화되어 특정 유형의 함수를 생성합니다.
정의
함수
template<typename T1, typename T2, ...typename Tn>
返回值 函数名(参数列表) {}
템플릿을 정의할 수 있습니다. 여기서 typename은 클래스로 대체될 수도 있습니다.
예를 들어 스왑 기능 템플릿은 다음과 같습니다.
template<typename T>
void Swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
그런 다음 함수와 마찬가지로 함수 템플릿을 사용할 수 있습니다.
int main()
{
int a = 10;
int b = 20;
Swap(a, b);
cout << a << " " << b << endl;
return 0;
}
템플릿에 의해 선언된 typename은 다음 함수에서만 유효하다는 점에 유의해야 합니다.
인스턴스화
함수 템플릿을 사용할 때 컴파일러는 함수 템플릿의 인스턴스화인 지정된 유형의 함수를 생성합니다.
생성 함수의 유형을 지정할 때 암시적 및 명시적 두 가지 방법이 있습니다.
암시적 인스턴스화
암시적 인스턴스화는 컴파일러가 호출 시 실제 매개변수에 따라 템플릿 매개변수의 유형을 자동으로 지정함을 의미합니다. 예를 들어
다음 코드는 다음과 같습니다.
template<typename T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
int b = 20;
cout << Add(a, b) << endl;
return 0;
}
이때 변수 a
와 b
둘 다 int이고 int
, 컴파일러는 둘 다 int인 T
실제 매개변수 두 개에서 자연스럽게 유형을 유추할 수 있어 int
이해하기 어렵지 않다.
그러나 실제 매개변수의 유형이 다른 경우 T
두 매개변수 유형 중 어떤 유형인지 판별할 수 없습니다.
int a = 10;
double b = 20;
//cout << Add(a, b) << endl; //错误代码,调用Add时模板参数不明确
템플릿에서는 승인 없이 유형 변환이 수행되지 않습니다. 예를 들어, 이 경우 유형이 T
모호할 때 int를 double로 변환할지 또는 double을 int로 변환할지 확실하지 않습니다.
물론 해결책은 매우 간단합니다.
- 매개변수를 전달할 때 실제 매개변수의 유형을 일관성 있게 명시적으로 변환할 수 있습니다.
template<typename T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double b = 20;
cout << Add(a, (int)b) << endl;
return 0;
}
유형 변환은 임시 변수를 생성하고 임시 변수는 상수라는 점에 유의해야 합니다. 따라서 템플릿의 형식 매개변수 유형을 const
수정하지 않으면 권한 증폭이 발생하므로 이 방법은 바람직하지 않습니다.
- 물론 명시적으로 type 을 지정하여
T
컴파일러가 암시적 유형 변환, 즉 명시적 인스턴스화를 시도하도록 할 수도 있습니다 .
명시적 인스턴스화
명시적 인스턴스화는 호출 시 함수 이름 <>
뒤에 템플릿 매개 변수의 유형을 명시적으로 지정하는函数名<模板参数列表>(实参列表);
것입니다. 예를 들어 위의 Add 템플릿 생성 함수 호출:
int main()
{
int a = 10;
double b = 20;
cout << Add<int>(a, b) << endl; // Add<int>(a, b)
return 0;
}
템플릿 매개변수의 타입이 지정되면 컴파일러는 매개변수를 전달할 때 암시적 타입 변환을 수행할 수 있으며 변환이 실패하면 오류가 보고됩니다.
알아야 할 사항은 다음과 같습니다.
- 비템플릿 함수는 동일한 이름의 함수 템플릿과 동시에 존재할 수 있으며 , 다른 조건이 같을 경우 비템플릿 함수가 먼저 호출되고 호출 시 템플릿에서 인스턴스가 생성되지 않습니다. 더 잘 일치하는 함수를 생성할 수 있는 경우 템플릿이 선택됩니다.
예를 들어:
int Add(const int& a, const int& b)
{
return a + b;
}
template<typename T1, typename T2>
T1 Add(const T1& a, const T2& b)
{
return a + b;
}
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1, 2.0) << endl;
return 0;
}
Add 함수가 처음 호출될 때 실제 매개변수 유형은 int이므로 비템플릿 함수가 먼저 호출됩니다.
Add 함수가 두 번째로 호출될 때 첫 번째 매개변수 유형은 int이고 두 번째 매개변수는 템플릿 함수에 의해 생성된 추가가 더 잘 일치할 수 있으므로 템플릿을 호출합니다.
3. 템플릿 함수는 자동 유형 변환을 허용하지 않지만 일반 함수는 자동 유형 변환을 수행할 수 있습니다.
클래스 템플릿
함수와 유사하게 유형 독립적인 클래스, 즉 클래스 템플릿을 정의할 수도 있습니다. 사용될 때 컴파일러에 의해 지정된 템플릿 매개변수 유형에 대한 클래스 유형으로 인스턴스화됩니다.
정의
함수
template<typename T1, typename T2, ...typename Tn>
class 类模板名 {};
템플릿을 정의할 수 있습니다. 여기서 typename은 클래스로 대체될 수도 있습니다.
예를 들어 간단한 스택을 작성할 수 있습니다.
template<typename T>
class Stack
{
public:
Stack(size_t size = 0, size_t capacity = 0) //构造函数
: _size(size)
, _capacity(capacity)
, _date(nullptr)
{
_date = new T[_capacity + 1]{
0 };
}
void push(T n) //压栈
{
if (_size == _capacity)
{
if (_capacity == 0)
{
reserve(6);
}
else
{
reserve(_capacity * 2);
}
}
_date[_size] = n;
++_size;
}
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
T* newdate = new T[capacity + 1]{
0 };
_capacity = capacity;
for (int i = 0; i < _size; ++i)
{
newdate[i] = _date[i];
}
delete[] _date;
_date = newdate;
}
}
T top()
{
return _date[_size - 1];
}
size_t size()
{
return _size;
}
~Stack() //析构函数
{
delete[] _date;
_size = 0;
_capacity = 0;
}
private:
size_t _size;
size_t _capacity;
T* _date;
};
int
유형 데이터 의 경우 :
int main()
{
Stack<int> nums;
nums.push(1);
nums.push(2);
nums.push(3);
nums.push(4);
nums.push(5);
cout << nums.top() << endl;
cout << nums.size() << endl;
return 0;
}
char
유형 데이터 의 경우 :
int main()
{
Stack<char> str;
str.push('a');
str.push('b');
str.push('c');
str.push('d');
str.push('e');
cout << str.top() << endl;
cout << str.size() << endl;
return 0;
}
서로 다른 템플릿 매개변수 유형에 대해 이 간단한 스택이 그 효과를 달성할 수 있다는 것을 찾는 것은 어렵지 않습니다.
인스턴스화
함수 템플릿과 달리 클래스 템플릿은 매개변수를 통해 템플릿 매개변수의 유형을 유추할 수 없으므로 클래스 템플릿의 인스턴스화는 <>
호출 후 클래스 템플릿 이름에 템플릿 매개변수를 명시적으로 나타내야 합니다.
위의 스택 템플릿 사용 예:
int main()
{
//实例化类模板并实例化类对象
Stack<char> str;
Stack<int> nums;
Stack<double> dnums;
//对不同类型的栈堆栈
str.push('a');
str.push('b');
nums.push(3);
nums.push(4);
dnums.push(20.0);
dnums.push(30.0);
//打印不同类型栈的栈顶元素及元素个数
cout << str.top() << endl;
cout << str.size() << endl;
cout << nums.top() << endl;
cout << nums.size() << endl;
cout << dnums.top() << endl;
cout << dnums.size() << endl;
return 0;
}
클래스 템플릿의 이름은 실제 클래스가 아니지만 인스턴스화 결과는 실제 클래스라는 점에 유의해야 합니다.
STL 소개
STL(표준 템플릿 라이브러리-표준 템플릿 라이브러리): 재사용 가능한 구성 요소 라이브러리일 뿐만 아니라 데이터 구조 및 알고리즘을 포함하는 소프트웨어 프레임워크인 C++ 표준 라이브러리의 중요한 부분입니다.
STL 6대 주요 구성 요소
컨테이너: string, vector, list, deque, map, set 등
알고리즘: 찾기, 교체, 역순 정렬 등
(이는 이해를 돕기 위한 것으로 차후에 차차 소개할 예정입니다.)
요약하다
이 시점에서 함수 템플릿 및 클래스 템플릿을 포함한 템플릿의 초기 콘텐츠 소개는 끝났습니다.
제가 특정 부분을 명확하게 소개하지 않았거나 특정 부분에 문제가 있다고 생각하시면 댓글란에 올려주세요.
이 글이 도움이 되셨다면 클릭 한번으로 연결되길 바랍니다
당신과 함께 발전하기를 바랍니다