记录一下学习C++模板时的知识点
模板参数省略
调用模板函数时可以省略模板参数,因为很容易从实参中推导出类型
1 2 3 4 5 6 7
| template<typename T> void print(T val) { std::cout << val << std::endl; }
print(42); print("hello");
|
类模板参数通常不可以省略,但是在C++17及以上时具有类模板参数推导的机制,可以省略。
1 2 3 4 5 6 7
| template<typename T> class Wrapper { public: Wrapper(T val) { ... } };
Wrapper w(42);
|
对于C++11等不支持类模板参数推导的情况,可以使用模板函数对模板类的构造进行封装,也能实现类似的效果
1 2 3 4 5 6 7 8 9 10 11 12
| template<typename T> class Wrapper { public: Wrapper(T val) { ... } };
template<typename T> Wrapper<T> make_wrapper(T val) { return Wrapper<T>(val); }
auto w = make_wrapper(42);
|
模板实例化的规则
先看下这段代码问题在哪里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include "bits/stdc++.h"
using namespace std;
template<typename T, typename... Args> void print(T firstArg, Args... args) { cout << firstArg << '\n'; if (sizeof...(args) > 0) { print(args...); } }
int main() { print(123, "hello", "world"); }
|
原因是因为if的判断是在运行时决定的,而模板的实例化是在编译时决定的,编译时无法知道运行时的状态。于是编译器看到print(args…)时必须确保它在任何情况下都合法,因此print函数需要补充一个无参数的重载版本。
可变参数模板中…的用法
基本使用
1 2
| template<typename... Args> void func(Args... args);
|
参数包解包
递归展开
1 2 3 4 5 6 7 8
| void print() {}
template<typename T, typename... Args> void print(T first, Args... rest) { std::cout << first << " "; print(rest...); }
|
折叠表达式
1 2 3 4
| template<typename... Args> auto sum(Args... args) { return (args + ...); }
|
二元运算符支持所有运算符
完美转发
完美转发的目的是保持参数的左/右值属性
1 2 3 4
| template<typename... Args> void wrapper(Args&&... args) { target_func(std::forward<Args>(args)...); }
|
参数翻倍
1 2 3 4
| template<typename... Args> void printDoubled(Args&... args) { print((args + args)...); }
|
参数加1
1 2 3 4
| template<typename... Args> void addOne(Args&... args) { print((args + 1)...); }
|
变参下标
1 2 3 4
| template<typename C, typename... Idx> void printElems(C& coll, Idx... idx) { print(coll[idx]...); }
|
sizeof…运算符
用于获取参数包大小,是一个编译器常量
1 2 3 4 5 6 7 8 9
| template<typename... Args> void func(Args... args) { constexpr size_t count = sizeof...(Args); constexpr size_t count2 = sizeof...(args); }
|
模板技巧
使用this→
对于类模板,如果它的基类也是依赖于模板参数的,那么对它而言即使 x 是继承而来的,使用 this->x
和 x
也不一定是等效的。比如
1 2 3 4 5 6 7 8 9 10 11 12 13
| template<typename T> class Base { public: void bar(); };
template<typename T> class Derived : Base<T> { public: void foo() { bar(); } };
|
Derived 中的 bar()永远不会被解析成 Base 中的 bar()。因此这样做要么会遇到错误,要么就是调用了其它地方的 bar(),比如可能是定义在其它地方的 global 的 bar()