diff --git "a/md/\347\254\254\344\270\200\351\203\250\345\210\206-\345\237\272\347\241\200\347\237\245\350\257\206/06\346\250\241\346\235\277\346\230\276\345\274\217\345\256\236\344\276\213\345\214\226\350\247\243\345\206\263\346\250\241\346\235\277\345\210\206\346\226\207\344\273\266\351\227\256\351\242\230.md" "b/md/\347\254\254\344\270\200\351\203\250\345\210\206-\345\237\272\347\241\200\347\237\245\350\257\206/06\346\250\241\346\235\277\346\230\276\345\274\217\345\256\236\344\276\213\345\214\226\350\247\243\345\206\263\346\250\241\346\235\277\345\210\206\346\226\207\344\273\266\351\227\256\351\242\230.md" index e45795c..0116618 100644 --- "a/md/\347\254\254\344\270\200\351\203\250\345\210\206-\345\237\272\347\241\200\347\237\245\350\257\206/06\346\250\241\346\235\277\346\230\276\345\274\217\345\256\236\344\276\213\345\214\226\350\247\243\345\206\263\346\250\241\346\235\277\345\210\206\346\226\207\344\273\266\351\227\256\351\242\230.md" +++ "b/md/\347\254\254\344\270\200\351\203\250\345\210\206-\345\237\272\347\241\200\347\237\245\350\257\206/06\346\250\241\346\235\277\346\230\276\345\274\217\345\256\236\344\276\213\345\214\226\350\247\243\345\206\263\346\250\241\346\235\277\345\210\206\346\226\207\344\273\266\351\227\256\351\242\230.md" @@ -174,6 +174,120 @@ template struct N::X2; // 类模板显式实例化定义 所以我们只需要显式实例化这个成员函数也能完成类模板分文件,如果有其他成员函数,那么我们就得都显式实例化它们才能使用,或者使用显式实例化类模板,它会实例化自己的所有成员。 +## 模板全特化与显式实例化的类似作用 + +本节是额外补充,虽然一般实际不会有人使用模板全特化来解决我们前面提到的那些问题:“*让编译器生成我们需要的代码*”。 + +我们写了这样一个示例: + +```cpp +// test.h +#include +template +void f(const T&); + +// test.cpp +#include +template +void f(const T& t){ + std::cout<< t <<'\n'; +} + +//main.cpp +#include "test.h" +int main(){ + f(66); +} +``` + +很显然,这段代码是无法通过编译的。[见](https://godbolt.org/z/WY1jd5aKM)。 + +原因在我们讲函数模板的时候就讲的很清楚了,模板只有实例化才会生成实际的函数定义,函数模板不是函数。`test.cpp` 中只是写了一个函数模板定义,编译器不会为我们生成任何函数定义,链接错误。 + +解决方法我相信大家也都懂,我们可以修改 `test.cpp` 为: + +```cpp +#include + +template +void f(const T& t){ + std::cout<< t <<'\n'; +} + +template void f(const int& t); // 显式实例化 +``` + +这很合理,不过我们本节的主题是,“**模板全特化**”,它实际有和显式实例化有类似效果,也是要求编译器在当前翻译单元生成我们需要的函数定义,将 `test.cpp` 修改为: + +```cpp +#include + +template +void f(const T& t){ + std::cout<< t <<'\n'; +} + +template<> +void f(const int& t){ + std::puts("f"); +} +``` + +同样可以通过编译。[见](https://godbolt.org/z/Knb6P3E6z)。 + +--- + +我们前面的内容也讲了类模板,它的和函数模板是不同的: + +> 类的完整定义不包括成员函数定义,理论上只要数据成员定义都有就行了。 + +模板全特化对类模板也有效,不过不是对类模板进行全特化,我们还是写一个示例: + +```cpp +// test.h +#include +template +struct X { + int a{}; + void f(); +}; + +// test.cpp +#include "test.h" +template +void X::f(){ + std::cout << "f: " << typeid(T).name() << "a: " << this->a << '\n'; +} + +//main.cpp +#include "test.h" +int main(){ + Xx; + x.f(); // 此处链接错误 +} +``` + +很显然,无法通过编译,[链接错误](https://godbolt.org/z/9dPfPcT8x)。 + +显式实例化类模板自然可以解决这个问题,我们修改 `test.cpp` 为其增加一行: + +```cpp +template struct X; +``` + +即可[通过编译](https://godbolt.org/z/a7qGoK34e)。 + +同样的,我们还可以选择为类模板 X 的成员函数 f 进行**全特化**,为 `test.cpp` 增加: + +```cpp +template<> +void X::f(){ + std::puts("😅:X::f"); +} +``` + +同样可以[通过编译](https://godbolt.org/z/T19sh5GWM)。 + ## 总结 如你所见,解决分文件问题很简单,显式实例化就完事了。 @@ -183,3 +297,5 @@ template struct N::X2; // 类模板显式实例化定义 模板必须实例化才能使用,实例化就会生成实际代码;有隐式实例化和显式实例化,我们平时粗略的说的“*模板只有使用了才会生成实际代码*”,其实是指使用模板的时候,就会**隐式实例化**,生成实际代码。 分文件自然没有隐式实例化了,那我们就得显式实例化,让模板生成我们想要的代码。 + +模板全特化有类似模板显式实例化的作用,不过我们一般知道这件事情即可。