文章目录
Template使用
函数返回
template <typename T>
T function() {
return T();
}
int main() {
cout << function<int>() << endl; // 显式指定模板参数类型
}
参数类型
template<class A, class B>
auto foo(A a, B b) {
return a + b;
}
template<typename T>
int f(T)
{
return 1;
}
template<typename T>
int f(T*)
{
return 2;
}
int main()
{
std::cout << f(0) << std::endl; // 自动检测模板参数类型
std::cout << f((int*)0) << std::endl; // 显式指定模板参数类型
}
C++ 14 auto 编译器推理返回类型
template<typename T1, typename T2>
auto max(T1 a, T2 b) {
return b < a ? a : b;
}
auto a = ::max(2, 3.2);
decltype返回类型后置
std::common_type_t<T1, T2>公共类型作为返回类型
Warning
模板参数、调用参数不匹配
#include <iostream>
// 多个参数的函数木板
template<typename RT, typename T1, typename T2>
RT max(T1 a, T2 b) {
using namespace std;
cout << "调用的自定义模板函数...... " << endl;
return b < a ? a : b;
}
double a = ::max(2, 3.2); // ERROR
double a = ::max<double, int, double>(2, 3.2); // CORRECT, 需要显式指定
double a = ::max<double>(2, 3.2); // CORRECT
- 模板参数和调用参数不能完全匹配的时候需要显式指定类型。
- 必须显式指定直到最后一个不能确定的所有模板类型。
无法解析外部符号
Problem:
将Template放在某个编译单元中出现“无法解析外部符号”的ERROR。
Solution:
- 方法1: 将类的声明和定义放在同一个template.h文件中。
- 方法2: 在template.h文件末尾include "template.cpp"文件(实测:无法编译通过。参考链接:模板,无法解析的外部符号)。
- 方法3: 在主函数所在的main.h中include .cpp文件(产生额外开销,所有引入main.h的文件均会加载)。
Analysis:
- 编译单元(translation unit):一个.cpp文件以及它所#include的所有.h文件(被扩展到包含它的.cpp文件里),然后编译器编译该.cpp文件为一个**.obj文件**。不同的cpp文件是分开编译的。再由连接器(linker)进行连接成为一个.exe文件。
- 主函数main编译时仅扩展template.h文件,没有对此模板进行实例化的过程,所编译的obj文件没有对类模板进行实例化,不会生成二进制文件。
- 在编译过程只有在同一个cpp文件及其include的文件中有引用函数模板的过程,函数模板才会转化成对应的函数。如:
int test(1,3)
编译的过程中会生成int test(int, int)
这个函数,将参数实例化为int(特化引用)。如果没有函数模板调用的过程,就不会生成任何函数。
模板调用顺序:
背景:template.h中声明了模板函数但是具体实现放在了template.cpp文件中,主函数中引用到testFunc的一个特化实例。编译过程中,template和Main分别编译为template.obj和Main.obj。
- 模板函数testFunc在编译(compile)期间并未生成具体二进制代码(除非有特化的引用、函数模板调用,testFunc(double d)将参数实例化为double),在main函数中也没有嵌入这个函数的代码,可能只是包含了一句call testFunc之类的。
- 编译阶段,在main函数中发现了testFunc的引用,但是main.obj中没有相关的可执行代码(编译器认为该函数在别处定义 =》 需要链接LINK),在main中虽然引用到testFunc但是只提供了一个call虚拟地址而没有实际的执行代码。
- 链接阶段,将各个模块(编译期间生成的很多*.obj文件)组织起来。在LINK的时候把testFunc“嵌入”进来,就像是一个子过程。在main中从调用处jump到这里即可,执行完毕再从“中断点”继续执行后续语句。
C++中使用函数模板出现“无法解析的外部符号”问题
C++ 函数模板的返回类型如何确定?
AUTO
C++ 11 trailing return type
auto func(T parameters) -> type;
// 等价于
type func(T parameters);
auto 关键字只是一个占位符。
C++ 11 类型推导decltype
template <typename U, typename V>
auto add(U a, V b) -> decltype(a + b);
通过 a + b (函数参数)的型别推断出 add 函数的返回类型。
C++14 auto
auto func(T param)
{
return xxx;
}
auto &
只能返回左值引用。auto &&
既能返回左值引用,又能返回右值引用。auto
只能返回一个型别。
C++14 类型推导decltype(简化)
decltype(auto) func(T parameter)
{
return xxx;
}
模板元编程
https://zhuanlan.zhihu.com/p/377145104
按条件匹配:enable_if
enable_if 利用了模板匹配的技巧和struct结构, 巧妙的将条件匹配分割成两种情况,
一种是true的情况: 为结构绑定一个type
一种是false的情况: 采取留空策略
template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test
// 这个实现叫做部分具体化(partial specialization), 即: 第一个参数 (<true>) 采用具体值.
// 当调用时传递的参数是true时, 一定会进入这个函数.
// 当调用时传递的参数是true且不提供其他参数时, <class _Ty> 会把自动合并上面一个enable_if的 <class _Ty = void>;
// 当调用时传递的参数是true且提供其他参数时, _Ty 会替换成传递参数的类型(放弃void).
template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test
using type = _Ty;
};
enable_if_t
基于模板的 SFINAE 和 匿名类型参数 的基础概念上封装。
enable_if_t 强制使用 enable_if 的 ::type 来触发 SFINAE 规则, 如果失败则跳过当前匹配进入下一个匹配.。
=》 面向数据编程
概要设计(架构设计) -》集成测试
详细设计 =》 单元测试