最后更新:2026-10-13
C++ 类模板优于函数模板¶
最近看到一个很有意思的模版问题,看看大家对函数模版与类模版的基础掌握的如何,对于下面这个例子会出现什么问题?
你一般使用什么方法进行修复?
template <typename T, int R>
struct foo {
foo(const T& v) : value_(v) {}
void bar() {
std::cout << "Generic" << std::endl;
for (int i = 0; i < R; ++i) std::cout << value_ << std::endl;
}
T value_;
};
template <>
void foo<float, 3>::bar() {
std::cout << "Float" << std::endl;
for (int i = 0; i < 3; ++i) std::cout << value_ << std::endl;
}
template <int R>
void foo<double, R>::bar() {
std::cout << "Double" << std::endl;
for (int i = 0; i < R; ++i) std::cout << value_ << std::endl;
}
答案是:最后两个特化直接编译不过。
(1)第一个 template <> void foo<float, 3>::bar() 是**全特化**,没问题;
(2)第二个 template <int R> void foo<double, R>::bar() 想做**偏特化**——但函数模板不允许偏特化!编译器把它当成一个新的函数模板重载来解析,签名又对不上,直接报错。
怎么修?类模板登场¶
C++ 的规则是:函数模板只能全特化,不能偏特化;类模板两者都行。所以惯用法是把"需要按类型分支的逻辑"从函数模板里抽出来,放进一个类模板(通常就是 trait/policy),对类做偏特化:
template <typename T, int R>
struct bar_impl {
static void run(foo<T, R>& f) {
std::cout << "Generic" << std::endl;
for (int i = 0; i < R; ++i) std::cout << f.value_ << std::endl;
}
};
template <int R>
struct bar_impl<double, R> { // 类模板偏特化:合法!
static void run(foo<double, R>& f) {
std::cout << "Double" << std::endl;
for (int i = 0; i < R; ++i) std::cout << f.value_ << std::endl;
}
};
template <typename T, int R>
void foo<T, R>::bar() { bar_impl<T, R>::run(*this); }
这也是 STL 的标准操作:iterator_traits、__type_traits 全都是"类模板承载偏特化"的路数。
小结¶
(1)函数模板:只能重载 + 全特化,不能偏特化;
(2)需要"对某类类型做特殊实现"时,把实现搬进类模板,用偏特化/trait 分发;
(3)C++11 之后很多场景还能用 if constexpr 直接在函数体里分支,连类模板都省了——但它和偏特化的取舍,留给你思考。
相关主题¶
- C++ 避免默认构造函数 | C++那些事
- C++ 类初始化列表惯用法 | C++那些事
- C++ copy-and-swap 惯用法 | C++那些事
- C++ 依赖注入 | C++那些事
- C++ enum class 强类型枚举 | C++那些事
- C++ Header-Only 与实现分离 | C++那些事
- C++ 延迟计算惯用法 | C++那些事
- C++ 查表法惯用法 | C++那些事
- C++ 成员指针与类型信息 | C++那些事
- C++ Mixin 惯用法 | C++那些事