我在初学C++模板时曾遇到一些问题。如果在头文件中声明一个模板类/函数,并且在源文件中对其进行定义,就比如说以下的代码
头文件 my_func.h
#pragma once #include <iostream> #ifndef MY_FUNC_H #define MY_FUNC_H template<typename T> void my_func(const T& val); #endif
源文件 my_func.cpp
#include "my_func.h" void my_func(const T& val) { std::cout<<val; }
然而在主函数中调用它,却无法通过编译。VS发出了如下的错误:
那么有没有解决办法呢?当然是有的,经过我的摸索,有以下几种解决办法。
1.直接定义
这是最简单粗暴的方法,那就是放弃分离定义和声明,直接都写在头文件中。
2.显式实例化模板
因为模板其实是在编译时并根据具体的类型对模板进行实例化,也就是说你想要调用的模板函数其实在编译时才生成了对应类型的函数。然而因为头文件和源文件分离,编译器无法得知你具体想要实例化的类型,所以才会出错。
解决的办法也很简单,就是在对应的源文件中显示实例化模板。
将源文件改为如下内容:
源文件 my_func.cpp
#include "my_func.h" template <typename T> void my_func(const T& val) { std::cout << val; } template void my_func(const int&);
这样就能在主函数中顺利调用对应的类型的模板函数了
main.cpp
my_func<int>(100);
同样的,模板类也需要显示实例化。
但是!这样做的缺点也很明显,如果你像上文中一样更改源文件中的定义,那么你也只能调用对应类型的函数,也就是说你想要使用什么类型的模板都必须提前手动实例化,这样的模板简直是毫无意义,毕竟模板的优势不就是能处理任何类型的数据嘛。
3.使用模块
模块(module)是C++20中新引入的特性,具有更快更方便的优点。使用模块可以避免这类问题。如果想要在VS中开启模块,点击:项目->属性->常规->C++语言标准,选择C++20或者C++latest后点击“确定”。
模块接口 my_func.ixx
import <iostream> export template <typename T> void my_func(const T& val) { std::cout << val; }
使用“export”关键字模板声明定义一气呵成,确实很简单。