제네릭 프로그래밍
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
제네릭 프로그래밍은 다양한 데이터 타입에 적용 가능한 일반적인 알고리즘을 구현하는 프로그래밍 기법이다. 1989년 머서와 스테파노프에 의해 개념이 정의되었으며, 코드 재사용성과 타입 안전성을 높이는 데 기여한다. C++, Java, C#, Ada, Haskell, D 등 여러 프로그래밍 언어에서 제네릭 프로그래밍을 지원하며, 각 언어별로 템플릿, 타입 클래스, 제네릭 유닛 등의 기능을 통해 구현된다.
더 읽어볼만한 페이지
- 제네릭 프로그래밍 - 다형성 (컴퓨터 과학)
다형성은 프로그래밍 언어에서 이름이 여러 자료형에 사용되거나 객체가 여러 타입으로 취급되는 능력으로, 코드 재사용성과 유연성을 높이며 임시 다형성, 매개변수 다형성, 서브타이핑 등으로 분류되고 객체 지향 프로그래밍의 중요한 특징이며 정적, 동적 다형성으로 구현된다. - 제네릭 프로그래밍 - 표준 템플릿 라이브러리
표준 템플릿 라이브러리(STL)는 알렉산더 스테파노프의 주도로 탄생한 C++ 라이브러리로서, 제네릭 프로그래밍을 지원하기 위해 컨테이너, 반복자, 알고리즘, 함수 객체 등의 핵심 구성 요소로 이루어져 있으며, HP에서 무료로 공개한 구현을 기반으로 다양한 구현체가 존재한다. - 프로그래밍 패러다임 - 지식 표현
지식 표현은 컴퓨터가 인간의 지식을 이해하고 활용하도록 정보를 구조화하는 기술이며, 표현력과 추론 효율성의 균형, 불확실성 처리 등을 핵심 과제로 다양한 기법과 의미 웹 기술을 활용한다. - 프로그래밍 패러다임 - 의도적 프로그래밍
의도적 프로그래밍은 프로그래머의 의도를 명확히 포착하고 활용하여 소프트웨어 개발 생산성을 향상시키기 위한 프로그래밍 패러다임으로, 트리 기반 저장소를 사용해 코드 의미 구조를 보존하고, WYSIWYG 환경에서 도메인 전문가와 협업하며, 코드 상세 수준 조절 및 자동 문서화를 통해 가독성과 유지보수성을 높이는 데 중점을 둔다.
제네릭 프로그래밍 | |
---|---|
개요 | |
종류 | 프로그래밍 패러다임 |
특징 | 일반화된 자료형과 알고리즘을 사용 |
개념 | |
목표 | 코드 재사용성 증대, 형식 안전성 확보, 성능 향상 |
핵심 요소 | 템플릿 (C++) 제네릭스 (Java, C#) 다형성 매개변수화 형식 |
장점 | |
코드 재사용성 | 여러 자료형에 적용 가능한 코드 작성 가능 |
형식 안전성 | 컴파일 시 형식 검사 강화 |
성능 | 특수화된 코드 생성으로 런타임 오버헤드 감소 |
단점 | |
코드 복잡성 | 제네릭 코드 작성 및 이해의 어려움 |
컴파일 시간 | 템플릿 특수화로 인한 컴파일 시간 증가 가능성 |
활용 예시 | |
자료 구조 | 벡터 리스트 맵 |
알고리즘 | 정렬 검색 변환 |
구현 | |
C++ | 템플릿 사용 |
Java | 제네릭스 사용 |
C# | 제네릭스 사용 |
그 외 언어 | 다양한 형태로 지원 (예: 타입스크립트의 제네릭) |
2. 역사적 배경
제네릭 프로그래밍은 1970년대 CLU와 Ada 같은 언어에 처음 도입되었으며,[32] 이후 BETA, C++, D, Eiffel, Java, DEC의 Trellis/Owl 등 다양한 객체 기반 및 객체 지향 언어에 채택되었다.
제네릭 프로그래밍은 타입을 추상화하여 코드 재사용성을 높이면서도, 정적 타입 언어의 타입 안전성을 유지한다.[33]
1995년에 발간된 디자인 패턴의 공저자들은 (Ada, Eiffel, Java, C#의) 제네릭스와 (C++의) 템플릿을 매개변수화된 타입(parameterized types)으로 언급하며, 형식을 지정하지 않고 정의하여 사용할 때 인수로 지정할 수 있게 해주는 강력한 기술이라고 설명했다.
머서와 스테파노프는 제네릭 프로그래밍을 다음과 같이 정의했다.[4]
> 제네릭 프로그래밍은 구체적이고 효율적인 알고리즘으로부터 추상화하여 다양한 유용한 소프트웨어를 생산하기 위해 서로 다른 데이터 표현과 결합될 수 있는 제네릭 알고리즘을 얻는 아이디어를 중심으로 한다.
"제네릭 프로그래밍" 패러다임은 알고리즘과 데이터 구조의 구체적인 예에서 유형에 대한 기본적인 요구 사항을 추상화하여 개념으로 공식화하는 소프트웨어 분해 접근 방식이며, 이는 추상 대수학에서 대수 이론의 추상화와 유사하다.[5] 이러한 프로그래밍 접근 방식의 초기 예는 Scheme과 Ada에서 구현되었지만,[6] 가장 잘 알려진 예는 표준 템플릿 라이브러리(STL)이다.[7][8]
알렉산더 스테파노프는 제네릭 프로그래밍에 대해 다음과 같이 썼다.
> 제네릭 프로그래밍은 알고리즘과 데이터 구조를 추상화하고 분류하는 것이다. 이는 커누스에게서 영감을 얻으며, 유형 이론에서 영감을 얻지 않는다. 그 목표는 유용하고 효율적이며 추상적인 알고리즘과 데이터 구조의 체계적인 카탈로그를 점진적으로 구축하는 것이다. 그러한 노력은 여전히 꿈이다.
> 나는 반복자 이론이 환 또는 바나흐 공간 이론이 수학에서 중심적인 것처럼 컴퓨터 과학에서 중심적이라고 믿는다.
비야네 스트롭스트룹은 다음과 같이 언급했다.
> 스테파노프를 따라가면서, 우리는 언어 기능을 언급하지 않고 제네릭 프로그래밍을 정의할 수 있다. 알고리즘과 데이터 구조를 구체적인 예에서 가장 일반적이고 추상적인 형태로 끌어올린다.
3. 주요 특징
머서와 스테파노프는 제네릭 프로그래밍을 "구체적이고 효율적인 알고리즘으로부터 추상화하여 다양한 유용한 소프트웨어를 생산하기 위해 서로 다른 데이터 표현과 결합될 수 있는 제네릭 알고리즘을 얻는 아이디어"라고 정의했다.[4]
"제네릭 프로그래밍" 패러다임은 알고리즘과 데이터 구조의 구체적인 예에서 유형에 대한 기본적인 요구 사항을 추상화하여 개념으로 공식화하는 소프트웨어 분해 접근 방식이다.[5] 이는 추상 대수학에서 대수 이론의 추상화와 유사하다.
비야네 스트롭스트룹은 "스테파노프를 따라가면서, 우리는 언어 기능을 언급하지 않고 제네릭 프로그래밍을 정의할 수 있다. 알고리즘과 데이터 구조를 구체적인 예에서 가장 일반적이고 추상적인 형태로 끌어올린다."라고 언급했다.[11]
제네릭 프로그래밍을 사용하지 않는 경우, C 언어나 파스칼과 같은 전통적인 정적 타입 언어에서, 정렬과 같은 알고리즘이나 연결 리스트와 같은 데이터 구조(컨테이너)를 구현할 때, 데이터 타입마다 각각 구현해야 한다. C++의 함수 템플릿이나 클래스 템플릿처럼, 제네릭 프로그래밍을 사용하면, 추상화된 타입에 대해 한 번만 기술한 알고리즘이나 데이터 구조를 다양한 구체 데이터 타입에 적용하여, 코드를 타입 안전하게 재사용할 수 있다.
3. 1. 타입 추상화
제네릭 프로그래밍은 구체적인 타입 대신 타입 매개변수를 사용하여 알고리즘이나 데이터 구조를 정의하는 방식이다. 머서와 스테파노프는 제네릭 프로그래밍을 "구체적이고 효율적인 알고리즘으로부터 추상화하여 다양한 유용한 소프트웨어를 생산하기 위해 서로 다른 데이터 표현과 결합될 수 있는 제네릭 알고리즘을 얻는 아이디어"라고 정의했다.[4]
"제네릭 프로그래밍" 패러다임은 알고리즘과 데이터 구조의 구체적인 예에서 유형에 대한 기본적인 요구 사항을 추상화하여 개념으로 공식화하는 소프트웨어 분해 접근 방식이다.[5] 이는 추상 대수학에서 대수 이론의 추상화와 유사하다.
예를 들어, ''N''개의 시퀀스 데이터 구조(단일 연결 목록, 벡터 등)와 ''M''개의 알고리즘(find
, sort
등)이 주어지면, 각 데이터 구조에 대해 각 알고리즘을 구현하는 대신, 제네릭 프로그래밍에서는 각 데이터 구조가 반복자 개념의 모델을 반환하고, 각 알고리즘은 이러한 반복자를 사용하여 작성된다. 따라서 ''N'' + ''M'' 개의 데이터 구조-알고리즘 조합만 구현하면 된다.
제네릭 프로그래밍의 특징은 타입을 추상화하여 코드의 재사용성을 향상시키면서도, 정적 타입 언어가 가지는 타입 안전성을 유지할 수 있다는 점이다.
제네릭 프로그래밍을 사용하지 않는 경우, C 언어나 파스칼과 같은 정적 타입 언어에서는 데이터 타입마다 알고리즘이나 데이터 구조를 각각 구현해야 한다. 예를 들어, 정수형 리스트, 배정밀도 부동소수점 수형 리스트, 문자열형 리스트 등 각 타입에 맞게 구현해야 한다. 제네릭 프로그래밍을 사용하면, 추상화된 타입에 대해 한 번만 기술한 알고리즘이나 데이터 구조를 다양한 구체 데이터 타입에 적용하여 코드를 타입 안전하게 재사용할 수 있다.
다음은 C++ 코드 예시이다.
```cpp
template
class LinkedList {
public:
// 양방향 연결 리스트의 노드.
class Node {
friend class LinkedList;
public:
T value;
private:
Node* prev;
Node* next;
private:
Node() : value(), prev(), next() {}
explicit Node(const T& value, Node* prev = NULL, Node* next = NULL) : value(value), prev(prev), next(next) {}
~Node() {}
public:
Node* getPrev() { return this->prev; }
Node* getNext() { return this->next; }
};
private:
Node dummy;
public:
LinkedList() : dummy() {
this->dummy.prev = &this->dummy;
this->dummy.next = &this->dummy;
}
~LinkedList() { this->clear(); }
size_t getSize() const { /* ... */ }
Node* getHead() { return this->dummy.next; }
Node* getTail() { return this->dummy.prev; }
Node* getSentinel() { return &this->dummy; }
static Node* insertBefore(Node* node, const T& value) {
assert(node);
assert(node->prev);
Node* temp = new Node(value, node->prev, node);
node->prev->next = temp;
node->prev = temp;
return temp;
}
static Node* insertAfter(Node* node, const T& value) {
assert(node);
assert(node->next);
Node* temp = new Node(value, node, node->next);
node->next->prev = temp;
node->next = temp;
return temp;
}
static void remove(Node*& node) {
assert(node);
if (node->prev) { node->prev->next = node->next; }
if (node->next) { node->next->prev = node->prev; }
delete node;
node = NULL;
}
void clear() {
for (Node* current = this->getHead(); current != this->getSentinel(); ) {
Node* temp = current;
current = current->next;
delete temp;
}
this->dummy.prev = &this->dummy;
this->dummy.next = &this->dummy;
}
};
LinkedList
LinkedList
LinkedList
```
위 코드는 양방향 연결 리스트를 제네릭하게 구현한 예시이다. `typename T`는 타입을 추상화하는 템플릿 매개변수이다.
비야네 스트롭스트룹은 "스테파노프를 따라가면서, 우리는 언어 기능을 언급하지 않고 제네릭 프로그래밍을 정의할 수 있다. 알고리즘과 데이터 구조를 구체적인 예에서 가장 일반적이고 추상적인 형태로 끌어올린다."라고 언급했다.[11]
3. 2. 코드 재사용성
제네릭 프로그래밍은 타입을 추상화하여 코드 재사용성을 높이는 프로그래밍 기법이다. 이는 정적 타입 언어의 타입 안전성을 유지하면서도, 다양한 타입에 대해 동일한 코드를 적용할 수 있게 해준다.
제네릭 프로그래밍을 사용하지 않으면, 예를 들어 C 언어나 파스칼과 같은 전통적인 정적 타입 언어에서는, 정렬 알고리즘이나 연결 리스트와 같은 데이터 구조를 구현할 때, 정수형, 부동소수점형, 문자열 등 각 데이터 타입마다 별도의 코드를 작성해야 했다. 이는 코드 중복을 야기하고 유지보수를 어렵게 만든다.
반면, C++의 템플릿과 같이 제네릭 프로그래밍을 지원하는 언어에서는, 추상화된 타입에 대해 한 번만 코드를 작성하고, 이를 다양한 구체 타입에 적용하여 재사용할 수 있다. 예를 들어, 다음은 C++로 작성된 양방향 연결 리스트의 예시이다.
```cpp
template
class LinkedList {
public:
// 양방향 연결 리스트의 노드.
class Node {
friend class LinkedList;
public:
T value;
private:
Node* prev;
Node* next;
private:
Node() : value(), prev(), next() {}
explicit Node(const T& value, Node* prev = NULL, Node* next = NULL) : value(value), prev(prev), next(next) {}
~Node() {}
public:
Node* getPrev() { return this->prev; }
Node* getNext() { return this->next; }
};
private:
Node dummy;
public:
LinkedList() : dummy() {
this->dummy.prev = &this->dummy;
this->dummy.next = &this->dummy;
}
~LinkedList() { this->clear(); }
size_t getSize() const { /* ... */ }
Node* getHead() { return this->dummy.next; }
Node* getTail() { return this->dummy.prev; }
Node* getSentinel() { return &this->dummy; }
static Node* insertBefore(Node* node, const T& value) {
assert(node);
assert(node->prev);
Node* temp = new Node(value, node->prev, node);
node->prev->next = temp;
node->prev = temp;
return temp;
}
static Node* insertAfter(Node* node, const T& value) {
assert(node);
assert(node->next);
Node* temp = new Node(value, node, node->next);
node->next->prev = temp;
node->next = temp;
return temp;
}
static void remove(Node*& node) {
assert(node);
if (node->prev) { node->prev->next = node->next; }
if (node->next) { node->next->prev = node->prev; }
delete node;
node = NULL;
}
void clear() {
for (Node* current = this->getHead(); current != this->getSentinel(); ) {
Node* temp = current;
current = current->next;
delete temp;
}
this->dummy.prev = &this->dummy;
this->dummy.next = &this->dummy;
}
};
LinkedList
LinkedList
LinkedList
```
위 코드에서 `typename T`는 템플릿에 의해 추상화되는 타입의 이름(플레이스 홀더)을 나타낸다. `LinkedList
이처럼 제네릭 프로그래밍은 코드 재사용성을 높여주고, 타입에 안전하게 작동하도록 보장한다.
3. 3. 타입 안전성
제네릭 프로그래밍은 타입을 추상화하여 코드 재사용성을 높이면서도 정적 타입 언어의 타입 안전성을 유지하는 특징을 가진다. 컴파일 시간에 타입 검사를 수행하여 런타임 오류를 줄인다.[33]
제네릭 프로그래밍을 사용하지 않으면, C 언어나 파스칼 같은 정적 타입 언어에서는 정렬 알고리즘이나 연결 리스트 같은 데이터 구조(컨테이너)를 만들 때, 데이터 타입마다 আলাদা আলাদা 코드를 작성해야 한다. 예를 들어, 정수형 리스트, 배정밀도 부동소수점 수형 리스트, 문자열 리스트 등 각각에 대해 코드를 만들어야 한다.
제네릭 프로그래밍을 지원하지 않는 언어에서 범용 코드를 작성해 재사용하려면, 메모리 효율이나 타입 안전성을 포기해야 할 수 있다. (예: 공용체나 범용 포인터형, 캐스트 등을 사용)
하지만 C++의 함수 템플릿이나 클래스 템플릿처럼 제네릭 프로그래밍을 사용하면, 추상화된 타입에 대해 한 번만 작성한 알고리즘이나 데이터 구조를 다양한 구체 데이터 타입에 적용하여, 타입 안전하게 코드를 재사용할 수 있다.
4. 프로그래밍 언어별 제네릭 프로그래밍 지원
다양한 프로그래밍 언어에서 제네릭 프로그래밍을 지원하며, 각 언어마다 구현 방식과 특징이 다르다. 제네릭 기능은 1970년대부터 ML, CLU, Ada와 같은 고급 언어에 존재해 왔으며, 이후 객체 기반 및 객체 지향 언어(예: BETA, C++, D, Eiffel, Java) 등 많은 언어에서 채택되었다.[32]
- '''Ada:''' 1977~1980년 설계 초기부터 제네릭을 지원한다. 표준 라이브러리는 제네릭을 사용하여 많은 서비스를 제공하며, Ada 2005는 C++ 표준 템플릿 라이브러리(STL)에서 영감을 받은 제네릭 컨테이너 라이브러리를 추가했다.[17]
- '''C++:''' 템플릿을 사용하여 제네릭 프로그래밍을 구현한다. 표준 템플릿 라이브러리(STL)는 일반적인 자료 구조와 알고리즘을 위한 템플릿 프레임워크를 제공한다.[7][8]
- '''Java:''' 제네릭스(generics)를 사용하여 제네릭 프로그래밍을 지원하며, 컴파일 시점에 타입 검사를 수행하고 런타임에는 타입 소거(type erasure)를 통해 제네릭 정보를 제거한다.[23]
- '''C#:''' .NET Framework 2.0부터 제네릭을 지원하며, 재현을 사용하여 런타임에서 제네릭을 일급 메커니즘으로 구현한다.[23]
- '''D:''' C++의 템플릿을 발전시킨 템플릿을 지원하며, C++ 템플릿 표현 대부분을 그대로 사용할 수 있고, 추가 기능도 제공한다.
- '''Haskell:''' 타입 클래스 메커니즘을 통해 제네릭 프로그래밍을 지원하며, 파생 인스턴스(derived instance)를 통해 타입 클래스의 인스턴스를 쉽게 선언할 수 있다.[14]
- '''ML:''' 매개변수 다형성(parametric polymorphism)과 펑터라고 불리는 제네릭 모듈을 활용한 제네릭 프로그래밍을 권장한다.[32]
- '''Verilog:''' 모듈 매개변수를 통해 제네릭 레지스터 배열과 같은 제네릭 요소를 만들 수 있다.[34]
- '''VHDL:''' Ada에서 파생되었으며, 엔티티, 아키텍처, 패키지 등에서 제네릭을 사용할 수 있다.
Objective-C와 같은 동적 타입 언어에서는 범용 타입을 사용하여 제네릭 프로그래밍과 유사한 효과를 얻을 수 있지만, 정적 타입의 통일성을 유지하기 어렵다.
4. 1. C++
C++는 제네릭 프로그래밍 기법을 구현하기 위해 템플릿을 사용한다. C++ 표준 템플릿 라이브러리(STL)는 일반적인 자료 구조와 알고리즘을 위한 템플릿 프레임워크를 제공한다.[7][8] C++의 템플릿은 실행 시간이 아닌 컴파일 시간에 코드를 미리 평가하는 방법인 템플릿 메타프로그래밍에도 사용될 수 있다. 템플릿 특수화를 사용하면 C++ 템플릿은 튜링 완전하다.```cpp
template
class List {
// 클래스 내용
};
List
List
```
위에서 `T`는 리스트가 생성될 때 지정된 타입에 대한 자리 표시자이다. 일반적으로 템플릿이라고 불리는 이러한 "타입-T 컨테이너"는 서브타입과 타입 시그니처와 같은 특정 계약이 유지되는 한, 클래스를 다른 데이터 타입과 함께 재사용할 수 있게 해준다.
```cpp
// "&"는 참조를 나타낸다
template
void Swap(T& a, T& b) { // 비슷한 기능을 하지만 더 안전하고 잠재적으로 빠른 함수가
// 표준 라이브러리 헤더
T temp = b;
b = a;
a = temp;
}
std::string world = "World!";
std::string hello = "Hello, ";
Swap(world, hello);
std::cout << world << hello << ‘\n’; // 출력은 "Hello, World!"이다.
```
위 코드는 C++에서 템플릿을 사용하여 제네릭 함수 `Swap`을 정의하는 예시이다. 이 함수는 타입 `T`에 관계없이 두 변수의 값을 교환한다.
4. 2. Java
Java는 제네릭스(generics)를 사용하여 제네릭 프로그래밍을 지원한다. 제네릭스는 컴파일 시점에 타입 검사를 수행하고, 런타임에는 타입 소거(type erasure)를 통해 제네릭 정보를 제거한다. 이렇게 하면 코드의 타입 안전성을 높이고 런타임 오버헤드를 줄일 수 있다.[23]예를 들어, `List
Java에서 제네릭은 컴파일 시 형식의 유효성을 검사하며 제네릭 형식 정보는 타입 소거를 통해 제거되어 부모 클래스의 형식 정보만 유지된다.[24][25] 예를 들어 `List
이 방식은 런타임에 제네릭 형식 정보를 참조할 수 없다는 부작용이 있다. 즉, 런타임에는 `List
와일드카드(wildcard)를 사용하여 제네릭 타입 매개변수의 범위를 제한할 수 있다. 와일드카드는 `?` 기호로 표현되며, 다음과 같은 형태로 사용될 수 있다.
- `List>`: 알 수 없는 타입의 요소를 가지는 리스트를 나타낸다.
- `List extends Number>`: `Number` 클래스를 상속받는 타입의 요소를 가지는 리스트를 나타낸다.
- `List super Number>`: `Number` 클래스의 상위 타입의 요소를 가지는 리스트를 나타낸다.
Java 제네릭스 구현상의 제약으로 인해 배열의 컴포넌트 타입을 특정할 방법이 없기 때문에 제네릭 타입의 배열을 생성하는 것은 불가능하다.
4. 3. C#
C#의 제네릭은 2005년 11월 .NET Framework 2.0의 일부로 추가되었으며, 1999년에 시작된 마이크로소프트 리서치의 연구 프로토타입을 기반으로 한다.[23] Java의 제네릭과 유사하지만, .NET 제네릭은 타입 소거를 적용하지 않으며, 재현을 사용하여 런타임에서 제네릭을 일급 메커니즘으로 구현한다. 이러한 설계는 제네릭 타입을 유지하면서 반사를 허용하고, 소거의 몇 가지 제한(예: 제네릭 배열을 만들 수 없음)을 완화하는 등 추가적인 기능을 제공한다.[24][25] 또한 런타임 캐스팅과 박싱 변환으로 인한 성능 저하가 없다. 원시 및 값 형식이 제네릭 인수로 사용될 때, 효율적인 제네릭 컬렉션 및 메서드를 허용하는 특수화된 구현을 얻는다. C++ 및 Java와 마찬가지로 Dictionary.NET은 `where` 키워드를 사용하여 제네릭 타입을 값 타입, 클래스, 생성자를 갖도록, 그리고 인터페이스를 구현하도록 제한하는 것을 포함하여 여섯 가지 종류의 제네릭 타입 제약 조건을 허용한다.[27] 다음은 인터페이스 제약 조건의 예시이다.
using System;
class Sample
{
static void Main()
{
int[] array = { 0, 1, 2, 3 };
MakeAtLeast
foreach (int i in array)
Console.WriteLine(i); // 결과 출력
Console.ReadKey(true);
}
static void MakeAtLeast
{
for (int i = 0; i < list.Length; i++)
if (list[i].CompareTo(lowest) < 0)
list[i] = lowest;
}
}
`MakeAtLeast()` 메서드는 제네릭 타입 `T`의 요소를 가진 배열에 대한 연산을 허용한다. 이 메서드의 타입 제약 조건은 메서드가 제네릭 `IComparable
위의 메서드는 제네릭 타입을 사용하지 않고 비 제네릭 `Array` 타입을 사용하여 간단하게 작성할 수도 있다. 그러나 배열은 반공변성이므로, 캐스팅은 타입 안전하지 않으며, 컴파일러는 제네릭 타입을 사용할 때 잡힐 수 있는 특정 오류를 찾을 수 없다. 또한 메서드는 배열 항목에 `object`로 접근해야 하며, 두 요소를 비교하려면 캐스팅이 필요하다. ( `int`와 같은 값 타입의 경우 이는 박싱 변환이 필요하지만, 표준 컬렉션 클래스에서 수행되는 것처럼 `Comparer
제네릭 .NET 클래스에서 정적 멤버의 주목할 만한 동작은 런타임 타입별 정적 멤버 인스턴스화이다(아래 예 참조).
// 제네릭 클래스
public class GenTest
{
// 정적 변수 - 리플렉션 시 각 타입에 대해 생성됨
static CountedInstances OnePerType = new CountedInstances();
// 데이터 멤버
private T _t;
// 간단한 생성자
public GenTest(T t)
{
_t = t;
}
}
// 클래스
public class CountedInstances
{
// 정적 변수 - 인스턴스당 한 번씩 증가
public static int Counter;
// 간단한 생성자
public CountedInstances()
{
// 객체 인스턴스화 중 카운터 1 증가
CountedInstances.Counter++;
}
}
// 메인 코드 진입점
// 실행 종료 시 CountedInstances.Counter = 2
GenTest
GenTest
GenTest
GenTest
```csharp
using System;
using System.Collections.Generic;
static int FirstIndexOfMax
{
if (list.Count == 0) {
return -1;
}
int index = -1;
for (int i = 0; i < list.Count; ++i) {
if ((index == -1 && list[i] != null) ||
(index >= 0 && list[index] != null && list[i] != null && list[index].CompareTo(list[i]) < 0)) {
index = i;
}
}
return index;
}
```
이 예제에서는 `FirstIndexOfMax` 메서드의 형식 매개변수 `T`에 대해, `
C++/CLI는 .NET의 제네릭과 C++의 템플릿을 모두 지원한다. 하지만 이들 사이에는 호환성이 없다.
4. 4. Ada
Ada는 1977년~1980년 설계 초기부터 제네릭을 지원한다.[17] 표준 라이브러리에서도 많은 서비스를 구현하기 위해 제네릭을 사용하고 있다. Ada2005에서는 1998년에 규격화된 C++의 표준 템플릿 라이브러리(STL)의 영향을 받은 광범위한 제네릭 컨테이너가 표준 라이브러리로 추가되었다.''제네릭 유닛''은 하나 이상의 ''제네릭 형식 매개변수''를 사용하는 패키지 또는 서브프로그램이다.[17]
''제네릭 형식 매개변수''는 값, 변수, 상수, 형식, 서브프로그램, 또는 심지어 다른 제네릭 유닛의 인스턴스일 수 있다.[17] 제네릭 형식의 경우, 구문은 이산 형식, 부동 소수점 수, 고정 소수점 수, 접근(포인터) 형식 등을 구분한다. 일부 형식 매개변수는 기본값을 가질 수 있다.
제네릭 유닛을 ''인스턴스화''하려면 프로그래머는 각 형식 매개변수에 대한 ''실제'' 매개변수를 전달해야 한다. 그러면 제네릭 인스턴스는 다른 유닛과 마찬가지로 동작한다. 예를 들어 루프 내에서 런타임에 제네릭 유닛을 인스턴스화하는 것이 가능하다.
4. 5. Haskell
Haskell의 타입 클래스 메커니즘은 제네릭 프로그래밍을 지원한다. Haskell에 미리 정의된 타입 클래스 중 6개(동등성을 비교할 수 있는 타입인 `Eq`와 값을 문자열로 렌더링할 수 있는 타입인 `Show` 포함)는 ''파생 인스턴스''(derived instance)를 지원하는 특별한 속성을 가지고 있다.[14] 이는 프로그래머가 새로운 타입을 정의할 때, 클래스 인스턴스를 선언할 때 일반적으로 필요한 클래스 메서드의 구현을 제공하지 않고도 이 타입이 이러한 특별한 타입 클래스의 인스턴스가 되도록 선언할 수 있다는 의미이다. 필요한 모든 메서드는 타입의 구조를 기반으로 "파생"되어 자동으로 구성된다. 예를 들어, 다음과 같은 이진 트리 타입의 선언은 이 타입이 `Eq` 및 `Show` 클래스의 인스턴스가 되도록 선언한다.data BinTree a = Leaf a | Node (BinTree a) a (BinTree a)
deriving (Eq, Show)
이렇게 하면 `T` 자체가 이러한 연산을 지원하는 경우 `BinTree T` 형식의 모든 타입에 대해 동등성 함수(
==
)와 문자열 표현 함수(show
)가 자동으로 정의된다.`Eq` 및 `Show`의 파생 인스턴스 지원은 해당 메서드
==
와 show
를 매개변수 다형 함수와 질적으로 다른 방식으로 제네릭하게 만든다. 이러한 "함수"(더 정확하게는, 타입 인덱싱된 함수 패밀리)는 다양한 타입의 값에 적용될 수 있으며, 모든 인자 타입에 대해 다르게 동작하지만, 새로운 타입에 대한 지원을 추가하는 데에는 거의 노력이 필요하지 않다. 랄프 힌제(Ralf Hinze, 2004)는 특정 프로그래밍 기술을 통해 사용자 정의 타입 클래스에 대해 유사한 효과를 얻을 수 있음을 보여주었다.[14] 다른 연구자들은 Haskell과 Haskell 확장의 맥락에서 이러한 종류의 제네릭성 및 다른 종류의 제네릭성에 대한 접근 방식을 제안했다.4. 6. D
D 언어는 C++의 템플릿을 발전시킨 템플릿을 지원한다. 대부분의 C++ 템플릿 표현은 D 언어에서도 그대로 사용할 수 있다. D 언어는 몇 가지 추가 기능을 제공한다.- D의 템플릿 매개변수는 타입과 기본값에만 국한되지 않고 (C++20 이전의 C++에서 그랬던 것처럼) 임의의 컴파일 시간 값 (문자열, 구조체 리터럴 등)과, 다른 템플릿 또는 템플릿 인스턴스화를 포함한 임의의 식별자에 대한 별칭도 허용한다.
- 템플릿 제약 조건과 `static if` 문은 각각 C++의 C++ 개념과 `if constexpr`에 대한 대안을 제공한다.
- `is(...)` 표현식을 사용하면 컴파일 시간에 객체의 특성을 검증하기 위해 투기적 인스턴스화를 수행할 수 있다.
- `auto` 키워드와 `typeof` 표현식을 사용하면 변수 선언 및 함수 반환 값에 대한 타입 추론이 가능하며, 이는 "볼드모트 타입" (전역 이름을 갖지 않는 타입)을 허용한다.[20]
D의 템플릿은 C++와 다른 구문을 사용한다. C++에서 템플릿 매개변수는 꺾쇠 괄호(`Template
D는 특성 기반 제네릭 프로그래밍을 사용하여 컴파일 타임 다형성을 제공한다. 예를 들어, 입력 범위는 다음과 같이 정의된 `isInputRange`에 의해 수행되는 검사를 만족하는 모든 타입으로 정의된다.
{{lang|d|
template isInputRange(R)
{
enum bool isInputRange = is(typeof(
(inout int = 0)
{
R r = R.init; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
}));
}
}}
입력 범위를 허용하는 함수는 템플릿 제약 조건에서 위의 템플릿을 사용할 수 있다.
{{lang|d|
auto fun(Range)(Range range)
if (isInputRange!Range)
{
// ...
}
}}
4. 7. 기타 언어
ML 패밀리는 매개변수 다형성(parametric polymorphism)과 펑터라고 불리는 제네릭 모듈을 활용한 제네릭 프로그래밍을 권장한다.[32]Verilog의 모듈은 하나 이상의 매개변수를 가질 수 있는데, 매개변수의 실제 값은 해당 모듈을 인스턴스화할 때 주어진다. 예를 들어 제네릭 레지스터 배열이 있으며, 배열의 너비가 매개변수로 주어진다. 이러한 배열을 제네릭 와이어 벡터와 결합함으로써 단일 모듈 구현을 사용하여 임의의 비트 폭을 가진 제네릭 버퍼 또는 메모리를 만들 수 있다.[34]
VHDL은 Ada에서 파생되었으며 제네릭 기능을 제공한다. VHDL에서 제네릭은 엔티티, 아키텍처, 패키지 및 패키지 인스턴스화에 사용할 수 있다.
전통적인 C 언어에서는 제네릭 프로그래밍을 지원하지 않지만, 공용체나 범용 포인터형과 캐스트를 활용하여 메모리 공간 효율이나 타입 안전성을 희생하여 범용적인 코드를 작성할 수 있다.
5. 제네릭 프로그래밍과 다른 프로그래밍 패러다임
제네릭 프로그래밍은 객체 지향 프로그래밍의 다형성과 구별된다. 제네릭 프로그래밍은 정적 다형성을, 객체 지향 프로그래밍은 동적 다형성을 제공한다.[33] 객체 지향 프로그래밍 언어는 서브타입(파생형)으로 슈퍼타입(기저형)의 동작(알고리즘)을 오버라이드함으로써 동적인 다형성을 갖추고 있으며, 동적인 다형성 또한 슈퍼타입에 의한 추상화와 서브타입에 의한 구체화를 실현한다.
참조
[1]
서적
Programming Languages: An Active Learning Approach
https://books.google[...]
Springer Science & Business Media
2008-12-15
[2]
간행물
A Logic for Computable Functions with Reflexive and Polymorphic Types
[3]
서적
Design Patterns
https://archive.org/[...]
Addison-Wesley
1994
[4]
서적
Generic Programming
https://stepanovpape[...]
[5]
서적
Elements of Programming
Addison-Wesley Professional
2009-06-19
[6]
서적
Proceedings of the 1987 annual ACM SIGAda international conference on Ada - SIGAda '87
[7]
문서
Alexander Stepanov and Meng Lee: The Standard Template Library. HP Laboratories Technical Report 95-11(R.1), 14 November 1995
[8]
문서
Matthew H. Austern: Generic programming and the STL: using and extending the C++ Standard Template Library. Addison-Wesley Longman Publishing Co., Inc. Boston, MA, USA 1998
[9]
문서
Jeremy G. Siek, Lie-Quan Lee, Andrew Lumsdaine: The Boost Graph Library: User Guide and Reference Manual. Addison-Wesley 2001
[10]
서적
Short History of STL
https://stepanovpape[...]
[11]
서적
Evolving a language in and for the real world: C++ 1991-2006
https://www.stroustr[...]
[12]
웹사이트
An Interview with A. Stepanov
https://www.stlport.[...]
[13]
서적
Generic Programming – an Introduction
https://www.cse.chal[...]
[14]
웹사이트
Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming
https://www.microsof[...]
Microsoft
2016-10-16
[15]
웹사이트
What is Generic Programming? (preprint LCSD'05)
https://lcsd05.cs.ta[...]
[16]
간행물
An extended comparative study of language support for generic programming (preprint)
[17]
웹사이트
Generic Units
https://www.adaic.or[...]
2024-04-25
[18]
문서
Stroustrup, Dos Reis (2003): Concepts - Design choices for template argument checking
https://www.stroustr[...]
[19]
서적
The Design and Evolution of C++
Addison-Wesley
[20]
웹사이트
Voldemort Types in D
https://www.drdobbs.[...]
2015-06-03
[21]
문서
Object-Oriented Software Construction,'' Prentice Hall, 1988, and ''Object-Oriented Software Construction, second edition,'' Prentice Hall, 1997.
[22]
문서
Eiffel: The Language,'' Prentice Hall, 1991.
[23]
문서
.NET/C# Generics History: Some Photos From Feb 1999
https://docs.microso[...]
[24]
문서
C#: Yesterday, Today, and Tomorrow: An Interview with Anders Hejlsberg
https://www.ondotnet[...]
[25]
문서
Generics in C#, Java, and C++
https://www.artima.c[...]
[26]
문서
Code Analysis CA1006: Do not nest generic types in member signatures
https://msdn.microso[...]
[27]
문서
Constraints on Type Parameters (C# Programming Guide)
https://msdn2.micros[...]
[28]
문서
The Generic Haskell User's Guide
https://www.cs.uu.nl[...]
[29]
문서
Verilog by Example, Section The Rest for Reference
[30]
문서
VHDL Reference
https://www.ics.uci.[...]
[31]
문서
WG14 N1516 Committee Draft — October 4, 2010
https://www.open-std[...]
[32]
웹사이트
Pure C++:Generic Programming Under .NET
http://msdn.microsof[...]
マイクロソフト・MSDNマガジン
2008-12-28
[33]
문서
統一モデリング言語 (UML) の用語では、それぞれ汎化 (generalization) および特化 (specialization) と呼ぶ。
[34]
문서
Verilog by Example, Section The Rest for Reference. Blaine C. Readler, Full Arc Press, 2011
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com