问题背景:为什么有三元运算符了,std还要实现诸多类似std::conditional的模板呢?
结论:完全实例化(展开)问题,避免模板编程带来的空间开销
初步探索:
- 首先来看下测试用例的代码
#include <iostream>
//#include <type_traits>//struct conditional
using namespace std;
/***********************v=a*(a-1)*(a-2)*...*1;
***********************/
//part one
template<int _Cond, typename _Iftrue, typename _Iffalse>
struct IfElse
{};template<typename _Iftrue, typename _Iffalse>
struct IfElse<false, _Iftrue, _Iffalse>
{typedef _Iffalse type;
};template<typename _Iftrue, typename _Iffalse>
struct IfElse<true, _Iftrue, _Iffalse>
{typedef _Iftrue type;
};template <int _Val>
struct Recursion{enum { val = Recursion<_Val-1>::val * _Val};
};
template<>
struct Recursion<1>{enum {val = 1};
};template <int Va,int Vb>
struct TestIfElse{typedef typename IfElse< (Va > Vb), Recursion<3>, Recursion<4>>::type ResultVal;
};void RecTest()
{int a,b;cin >> a;cin >> b;int v = a > b ? Recursion<3>::val : Recursion<4>::val;v = TestIfElse<1,2>::ResultVal::val;v = 1 > 2 ? Recursion<3>::val : Recursion<4>::val;
}//part two
class Int{
public:Int()=delete;~Int()=default;Int(int _v):val(_v){ cout << "constructor: " << val <<endl; };const int val;
};template<int _Val>
class IntRecursion{
public:const int Val=Int(_Val).val * IntRecursion<_Val-1>().Val ;
};
template<>
class IntRecursion<1>{
public:const int Val=Int(1).val ;
};void IntRecTest(int a,int b)
{int v = a > b ? IntRecursion<4>().Val : IntRecursion<3>().Val;v = 1 > 2 ? IntRecursion<4>().Val : IntRecursion<3>().Val;cout << v <<endl;
}int main()
{RecTest();IntRecTest(1,2);return 0;
}
- 在part one中的用例最后是由RecTest函数来测试的,在测试之前可以先猜猜这个函数中的最后三行都有啥区别,这个也是原因所在
来看看汇编代码,在图中的14~25行对应着函数的最后三行代码,可以看出,使用了TestIfElse的和三元运算符已经确定的情况下一样(看TestIfelse里的typedef,实现情形和14行的情形是一样的(a和b都还不确定),只计算出来Recursion<4>的值;而14行那个语句却计算了参数值为3和4的值(18行和19行),这个可能与三元运算符的机制相关。这就可以基本猜到,使用模板进行比较,可以少实例化(准确来说,是实例化对象,而代码层层面都是一样的,意思就是说,模板解析后22行和14行都计算了参数3和4的值,但是就是有没有实例化的问题,/*因为模板一有变化就是一种不同的类型*/);
- 接着再来看part two的汇编代码
在part two中,和在part one中实现的东西基本是一样的,这次的参数,我封装了一个Int类来看它构造了几个对象
可以看到第19和31行,它和part one中,ab参数都不确定时候一样,调用了两个函数,而当ab确定时候,它却只调用了参数为3的(45行),这就验证了上面的结论。
而在打印中,却这打印了两次参数为3的,没有打印参数为4的(即19行的值嘞?),我想,这个是因为这个是因为与class Int相关,因为在IntRecursion模板中使用的是一个类对象,它是在运行时,在栈分配的空间,虽然它在汇编代码里面是实例了,但是在运行的时候没有实例化改对象,因此没有打印。
其他杂言
- 模板的本质还是对象类型,它是在编译时就已经实例化好了(这里的实例化和类的实例化有些不一样,可以理解成类似宏展开),而类的实例化是要在运行时
- 我记得在《C++ template》中有提到过,模板的递归有限制(好像是不能超过14还是17层),测试了,是可以的,可能是那本书是基于98版的
- 模板编程是以空间换运行效率
- 很久没碰模板了,有些生疏,有问题欢迎留言