stl源码中,有个很基础的类型conditional_t,conditional_t有什么用?在回答这个问题之前,先看几个简单的例子。
现在有如下需求,有两个类A、B,如下:
struct A
{
int a= 10;
};
struct B
{
float b = 200.0f;
};
问题1:现在新加一个类O,我希望它有时候继承A,有时候继承B,该怎么办?
我们知道,一般继承情况下,O要么继承A,要么继承B,要么多继承A、B,怎么会既可能继承A,又可能继承B呢?从描述中,我们得出一个信息:O继承的类型是不固定的。而C++中,应付可变类型的技巧是什么?模板!Good,解决方法诞生了!
template<typename T>
struct O:public T
{};
//使用代码如下
O<A> o1; //O继承A
O<B> o2; //O继承B
接下来,继续看一个稍微变化的例子,以上代码中,O继承谁,完全是O自己说了算。
问题2:如果由外部给定一个参数,然后O根据这个参数来决定继承A还是B,该怎么办?
template<typename T>
void function(T t)
{
O ? //不同的T如何生成不同的O?
}
O继承的类型依然不固定,所以还是从模板编程下手,本情况下,外部还要传入参数,可见O的模板中必须接受一个外部参数。我们可以利用模板特化,写出如下代码:
template<typename T>
struct O: public A
{};
template<>
struct O<int>: public B
{};
template<typename T>
void function(T t)
{
O<T> o;
}
//使用代码如下
function<int>(); //其中O<int> o; O继承B
function<char>(); //其中O<char> o; O继承A
这里的O有瑕疵,什么瑕疵?这种代码写完后,O只能继承A或者B了,解决方法也很简单,把A、B也抽到模板参数里面,于是有如下代码
template<typename T, typename A, typename B>
struct O: public A
{};
template<typename A, typename B>
struct O<int, A, B>: public B
{};
template<typename T>
void function(T t)
{
O<T,A,B> o;
}
//使用代码如下
function<int>(); //其中O<int> o; O继承B
function<char>(); //其中O<char> o; O继承A
这样function里面,O可以通过修改模板的实际参数,来选择继承别的类。
问题3:O解决了选择继承的问题,我现在又写了一个新的类C,我希望C也能像O那样,怎么办?难道要像捣鼓O那样再捣鼓C一次?
再捣鼓C一次,明显是一种糟糕的做法,我们很希望有如下这种代码:
template<typename T, typename A, typename B>
class O:public <..把选择AB的过程放到这里解决..>
{};
这样,假如C也需要这个功能,只要把public后面的东西摘过去就行了,和O的本体代码不发生任何纠纷。<....>最好返回一个type,这个type由T A B三个模板参数决定,吸取之前两个方案的经验和技巧,我们可以写出如下代码:
template<typename T, typename A, typename B>
struct select_type
{
using Type = A;
};
template<typename A, typename B>
struct select_type <int,A,B>
{
using Type = B;
};
//这个select_t起到一个转接的作用
template<typename T, typename A, typename B>
using select_t = typename select_type<T, A, B>::Type;
template<typename T,typename A,typename B>
struct O :public select_t<T, A, B>
{
};
//使用代码
O<char, A, B > o1; //O继承A
O<int , A, B > o1; //O继承B
Good!我们成功把选择A、B的过程从O中剥离出来了,如果C也要这样,直接把public select<T,A,B>摘到C后面去就行了!方便!
好了,例子看完了,说回conditional_t,让我们看看conditional_t的源码:
// STRUCT TEMPLATE conditional
template<bool _Test,
class _Ty1,
class _Ty2>
struct conditional
{ // type is _Ty2 for assumed !_Test
using type = _Ty2;
};
template<class _Ty1,
class _Ty2>
struct conditional<true, _Ty1, _Ty2>
{ // type is _Ty1 for _Test
using type = _Ty1;
};
template<bool _Test,
class _Ty1,
class _Ty2>
using conditional_t = typename conditional<_Test, _Ty1, _Ty2>::type;
仔细一看,这和我们写的select_t几乎一模一样!所以conditional_t的设计目标就很明显了:在编译期,给定不同的参数,返回不同的类型!