语言地基
从输入输出、类型、表达式到函数,把“能编译”变成能解释。
模板描述一套适用于多种类型的代码。编译器在调用处推断 T,再实例化具体版本;错误往往出现在实例化阶段。
template <typename T>
const T& larger(const T& a, const T& b) {
return a < b ? b : a;
}类模板可以把类型作为参数,例如 vector
template <typename T>
class Box {
T value_;
public: T get() const { return value_; }
};推断会考虑引用、const、数组和函数指针的特殊规则。传值会发生数组到指针的退化,传引用可以保留更多类型信息。
template <typename T>
void inspect(const T& value);
int a[3]{}; inspect(a);普通函数、模板和不同模板参数之间会参与重载决议。完全特化可以为特殊类型提供版本,但不要用特化掩盖设计问题。
template <typename T>
void print(const T&);
template <> void print(const char* const&);参数包可以接收任意数量参数,std::forward 保留参数原本的左值/右值属性,常用于工厂和包装函数。
template <typename... Args>
auto make(Args&&... args) {
return Widget(std::forward<Args>(args)...);
}实现一个模板函数 print_all,接收任意数量参数并用空格分隔输出;再让它支持一个 vector。
template <typename T, typename... Rest>
void print_all(const T& first, const Rest&... rest);template <typename T, typename... Rest>
void print_all(const T& first, const Rest&... rest);模板必须在实例化处看得到定义,通常把实现写在头文件。普通函数可以只在头文件声明,因为链接器之后再找定义。
// widget.h
template <typename T> T max_value(T, T) { return ...; }显式实例化可以提前生成某个类型的模板,extern template 告诉其他文件不要重复生成,适合大型项目减少编译时间。
extern template class std::vector<int>;
template class Box<int>;模板参数可以是类型、整数、指针或引用等非类型参数。array 的大小就是典型非类型参数。
template <typename T, std::size_t N>
std::size_t count(const T (&)[N]) { return N; }当返回类型依赖参数时,可以用 auto 加尾置 -> decltype 表达返回类型。decltype((x)) 可能得到引用,括号会改变规则。
template <typename It>
auto first(It it) -> decltype(*it) { return *it; }特化为某个具体类型提供不同实现;偏特化为一类类型提供实现,但函数模板不能偏特化,只能重载。
template <typename T> struct Traits;
template <> struct Traits<int> { using type = long; };先自己回答,再展开解释。
模板定义为什么通常放在头文件?
完美转发中的 forward 做什么?
模板实参推断会自动把两个不同类型都转成同一类型吗?
类模板和函数模板的特化规则相同吗?
完成本章后,建议进入第 17 章;也可以回到首页查看知识地图或打开交互实验。
输入一组整数,逐个 push_back,观察 size、capacity 和重新分配次数。