C#의 제네릭(Generics)과 C++의 템플릿(Templates)은 모두 매개변수화된 타입(parameterized types)을 지원하여 코드 재사용성과 타입 안정성을 높여주는 강력한 기능입니다. 하지만 두 기능은 설계 철학과 동작 방식에서 여러 중요한 차이점을 보입니다. 이 글에서는 C# 제네릭과 C++ 템플릿의 주요 차이점을 항목별로 자세히 살펴보겠습니다.
1. 유연성(Flexibility)
C++ 템플릿은 C# 제네릭보다 훨씬 더 유연합니다. C++ 템플릿은 컴파일 시점에 매크로처럼 확장되며, 연산자 오버로딩, 비타입 매개변수 등 다양한 기능을 활용할 수 있어 복잡한 메타프로그래밍이 가능합니다. 반면 C# 제네릭은 언어 차원에서 더 엄격한 규칙을 따르기 때문에 상대적으로 유연성이 떨어지지만, 그만큼 예측 가능하고 안전한 동작을 보장합니다.
2. 명시적 특수화(Explicit Specialization)
C++ 템플릿은 특정 타입에 대해 다른 구현을 제공하는 명시적 특수화를 지원합니다. 예를 들어, 일반적인 타입에는 기본 구현을 사용하되, 특정 타입에 대해서는 최적화된 별도의 구현을 정의할 수 있습니다. 그러나 C#은 명시적 특수화를 지원하지 않으므로, 모든 타입 인자에 대해 동일한 구현이 적용됩니다.
3. 타입 매개변수(Type Parameter)의 제약
C#에서는 타입 매개변수를 해당 제네릭 타입의 기본 클래스(base class)로 사용할 수 없습니다. 또한 C#은 타입 매개변수에 기본 타입(default type)을 지정하는 것도 허용하지 않습니다. 즉, C++처럼 template<class T = int>와 같은 형태의 기본값 설정이 불가능하며, 호출 시 항상 타입 인자를 명시해야 합니다.
4. 컴파일 시점과 런타임 지원(Run-Time)
두 기능의 가장 큰 차이 중 하나는 처리 시점입니다. C++ 템플릿은 컴파일 타임 모델로 동작합니다. 컴파일러가 템플릿을 각 타입별로 실제 코드를 생성하는 방식(코드 인스턴스화)으로 처리하기 때문에, 실행 파일에는 각 타입에 대한 코드가 모두 포함됩니다. 반면 C# 제네릭은 컴파일 타임과 런타임 모두를 지원합니다. 제네릭 타입 정보가 IL(중간 언어)에 유지되고, .NET 런타임이 이를 인식하여 타입 안정성을 검사할 수 있습니다. 덕분에 리플렉션(reflection)으로 제네릭 타입 정보를 조회하는 것도 가능합니다.
5. 비타입 템플릿 매개변수(Non-type Template Parameters)
C++ 템플릿은 타입뿐만 아니라 정수 값, 포인터 등 비타입(non-type) 매개변수도 받을 수 있습니다. 예를 들어 template<int N>처럼 컴파일 타임 상수를 매개변수로 전달하여 배열 크기 등을 고정할 수 있습니다. 하지만 C# 제네릭은 타입 매개변수만 허용하므로, 이러한 비타입 매개변수는 사용할 수 없습니다.
6. 부분 특수화(Partial Specialization)
C++ 템플릿은 여러 타입 매개변수 중 일부만 특수화하는 부분 특수화를 지원합니다. 이를 통해 조건에 따라 다양한 구현을 선택하는 정교한 설계가 가능합니다. 반면 C#은 부분 특수화조차 지원하지 않습니다. C#에서 유사한 효과를 내려면 제약 조건(constraints), 오버로딩, 런타임 분기 등 다른 기법을 활용해야 합니다.
정리: 어떤 것을 선택해야 할까?
두 기능은 단순히 문법이 다른 수준이 아니라, 컴파일 타임 코드 생성(C++)과 런타임 타입 정보 유지(C#)라는 근본적으로 다른 접근 방식을 취합니다. 최대한의 성능과 유연성, 컴파일 타임 메타프로그래밍이 필요하다면 C++ 템플릿이 유리하고, 플랫폼 독립성, 타입 안정성, 리플렉션 기반의 런타임 지원이 중요하다면 C# 제네릭이 더 적합한 선택입니다. 각 언어의 설계 목표를 이해하고 프로젝트 요구 사항에 맞게 활용하는 것이 중요합니다.