C++ Template Specialization | Full vs Partial· Traits
이 글의 핵심
Master template specialization in C++: full and partial specialization, overload resolution vs partial ordering, ODR, ambiguity, and real patterns for traits and serialization.
Introduction
Template specialization supplies a different implementation than the primary template for specific types or type patterns. Use it for per-type optimization, exceptions to generic behavior, and type-trait-style metaprogramming.
1. Full specialization
Function template full specialization
#include <iostream>
#include <string>
template<typename T>
void print(T v) {
std::cout << v << std::endl;
}
template<>
void print<const char*>(const char* v) {
std::cout << '"' << v << '"' << std::endl;
}
template<>
void print<std::string>(std::string v) {
std::cout << '"' << v << '"' << std::endl;
}
int main() {
print(42);
print(3.14);
print("hello");
print(std::string("world"));
return 0;
}
Resolution: The most specific viable specialization wins (e.g. const char* vs the primary T).
Class template full specialization
template<typename T>
class Storage {
T data;
public:
Storage(T d) : data(d) {}
void print() {
std::cout << "Generic: " << data << std::endl;
}
};
template<>
class Storage<bool> {
bool data;
public:
Storage(bool d) : data(d) {}
void print() {
std::cout << "Bool: " << (data ? "true" : "false") << std::endl;
}
bool get() const { return data; }
};
2. Partial specialization
Pointer partial specialization
template<typename T>
class Wrapper {
T value;
public:
Wrapper(T v) : value(v) {}
void print() {
std::cout << "Value: " << value << std::endl;
}
};
template<typename T>
class Wrapper<T*> {
T* ptr;
public:
Wrapper(T* p) : ptr(p) {}
void print() {
if (ptr) {
std::cout << "Pointer: *" << *ptr << " (at " << ptr << ")" << std::endl;
} else {
std::cout << "Pointer: nullptr" << std::endl;
}
}
};
Array partial specialization
template<typename T>
struct ArrayTraits {
static constexpr bool is_array = false;
static constexpr size_t size = 0;
};
template<typename T, size_t N>
struct ArrayTraits<T[N]> {
static constexpr bool is_array = true;
static constexpr size_t size = N;
using element_type = T;
};
3. Practical example: type traits
is_pointer sketch
template<typename T>
struct is_pointer {
static constexpr bool value = false;
};
template<typename T>
struct is_pointer<T*> {
static constexpr bool value = true;
};
remove_const sketch
template<typename T>
struct remove_const {
using type = T;
};
template<typename T>
struct remove_const<const T> {
using type = T;
};
4. Common problems
Problem 1: No partial specialization for function templates
Use overloads instead:
template<typename T>
void func(T value) { /* generic */ }
template<typename T>
void func(T* value) { /* pointer */ }
Problem 2: Overload / specialization precedence
Roughly: full specialization beats partial beats primary—see the standard’s partial ordering rules for your exact case.
Problem 3: ODR and specializations in headers
Avoid multiple definitions of the same full specialization across TUs unless inline rules apply—often declare in a header and define in one .cpp, or mark appropriately.
Problem 4: Ambiguous partial specializations
Disambiguate with a more specific full specialization or by refactoring patterns.
5. Example: serialization system
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
template<typename T>
struct Serializer {
static std::string serialize(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
};
template<>
struct Serializer<std::string> {
static std::string serialize(const std::string& value) {
return "\"" + value + "\"";
}
};
template<>
struct Serializer<bool> {
static std::string serialize(const bool& value) {
return value ? "true" : "false";
}
};
template<typename T>
struct Serializer<std::vector<T>> {
static std::string serialize(const std::vector<T>& vec) {
std::string result = "[";
for (size_t i = 0; i < vec.size(); ++i) {
if (i > 0) result += ", ";
result += Serializer<T>::serialize(vec[i]);
}
result += "]";
return result;
}
};
template<typename T>
struct Serializer<T*> {
static std::string serialize(T* const& ptr) {
if (ptr) {
return "*" + Serializer<T>::serialize(*ptr);
}
return "null";
}
};
6. Example: type-trait-style library
#include <iostream>
#include <type_traits>
template<typename T>
struct TypeTraits {
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_array = false;
static constexpr size_t size = sizeof(T);
static std::string name() { return "Unknown"; }
};
template<typename T>
struct TypeTraits<T*> {
static constexpr bool is_pointer = true;
static constexpr bool is_const = false;
static constexpr bool is_array = false;
static constexpr size_t size = sizeof(void*);
static std::string name() {
return TypeTraits<T>::name() + "*";
}
};
template<typename T>
struct TypeTraits<const T> {
static constexpr bool is_pointer = TypeTraits<T>::is_pointer;
static constexpr bool is_const = true;
static constexpr bool is_array = TypeTraits<T>::is_array;
static constexpr size_t size = sizeof(T);
static std::string name() {
return "const " + TypeTraits<T>::name();
}
};
template<typename T, size_t N>
struct TypeTraits<T[N]> {
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_array = true;
static constexpr size_t size = sizeof(T) * N;
static std::string name() {
return TypeTraits<T>::name() + "[" + std::to_string(N) + "]";
}
};
template<>
struct TypeTraits<int> {
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_array = false;
static constexpr size_t size = sizeof(int);
static std::string name() { return "int"; }
};
template<>
struct TypeTraits<double> {
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_array = false;
static constexpr size_t size = sizeof(double);
static std::string name() { return "double"; }
};
Summary
- Full specialization: alternate implementation for one concrete substitution.
- Partial specialization: alternate implementation for a pattern (class templates only).
- Functions: no partial specialization—use overloading.
- Precedence: more specific template wins.
- Production: traits, serialization, optimized paths.
Comparison
| Full | Partial | |
|---|---|---|
| Applies to | Function or class templates | Class templates only |
| Matches | Concrete type (int, bool) | Pattern (T*, const T) |
| Syntax | template<> | template<typename T> |
Related posts
Keywords
C++, template specialization, partial specialization, full specialization, traits.
See also
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Master template specialization in C++: full and partial specialization, overload resolution vs partial ordering, ODR, am… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 템플릿 특수화 완벽 가이드 | 완전·부분 특수화, 문제 시나리오, 프로덕션 패턴
- C++ 클래스 템플릿 | 제네릭 컨테이너와 부분 특수화
- C++ 템플릿 인자 추론 | template argument deduction 가이드
이 글에서 다루는 키워드 (관련 검색어)
C++, Templates, Specialization, Partial Specialization, SFINAE 등으로 검색하시면 이 글이 도움이 됩니다.