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
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Master template specialization in C++: full and partial specialization, overload resolution vs partial ordering, ODR, am…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- C++ 템플릿 특수화 완벽 가이드 | 완전·부분 특수화, 문제 시나리오, 프로덕션 패턴
- C++ 클래스 템플릿 | 제네릭 컨테이너와 부분 특수화
- C++ 템플릿 인자 추론 | template argument deduction 가이드
Keywords Covered in This Article (Related Search Terms)
This article covers C++, Templates, Specialization, Partial Specialization, SFINAE.