我们知道在C++模板编程中如果我们特化或是偏特化某个模板类, 我们需要重写整个模板类中的所有函数, 但是这些代码通常是非常相似的, 甚至在某些情况下可能只有一两个函数会不一样,其他函数都是一样的。在这种情况下,同时存在多份相同的代码,对我们维护这些代码是非常不利的, 我们最好只需要特化其中不一样的那个函数。
比如下面这个模板类:
struct Base
{
// other function
// ....
void Func(){ cout << " primary function " << endl; }
};
void test1()
{
Base< int, 1> a;
a.Func();
Base< int, 16> b;
b.Func();
}
int main()
{
test1();
}
只有当B等于16时, Func这个函数需要特化, 但是其他函数无论什么情况下都是一样的。
下面是我们的一些可能解决方案:
方法1:
struct Base
{
// other function
// ....
void Func(){ cout << " specialization function " << endl; }
};
点评:通过偏特化实现,需要重写所有的类成员方法。
方法2:
struct Base
{
// other function
// ....
void Func()
{
if(B == 16)
{
cout << " primary function " << endl;
}
else
{
cout << " specialization function " << endl;
}
}
};
点评: 通过运行时判断, 容易理解,但是相对低效。
方法3:
struct Base
{
// other function
// ....
void Func()
{
#if B!=16
cout << " primary function " << endl;
#else
cout << " specialization function " << endl;
#endif
}
};
点评: 试图通过预编译来实现,但是这个方法是错误的。C++模板编译包括预编译,语法检查,模板实例化等阶段,在预编译阶段模板参数都还没有实例化呢。
方法4:
struct Base
{
// other function
// ....
template
struct FuncObj
{
void operator()()
{
cout<< " primary function "<
};
template<>
struct FuncObj< 16>
{
void operator()()
{
cout<< " specialization function "<
};
FuncObj Func;
};
点评: 通过成员类以防函数的形式特化, 增加了类成员变量。
方法5:
struct Base
{
// other function
// ....
template
void FuncImpl()
{
cout<< " primary function "<
template<>
void FuncImpl< 16>()
{
cout<< " specialization function "<
void Func()
{
FuncImpl();
}
};
点评:通过类成员模板函数特化来实现。
方法6:
struct Base
{
// other function
// ....
template
class Int2Type
{
enum { value = N };
};
template
void FuncImpl( const Int2Type
{
cout<< " primary function "<
void FuncImpl( const Int2Type< 16>)
{
cout<< " specialization function "<
void Func()
{
FuncImpl(Int2Type());
}
};
点评: 通过将int根据值的不同转成不同的类型,然后通过函数重载实现。
方法7:
{
template < bool,typename T,typename> struct conditional { typedef T type; };
template
}
template< class T, unsigned B>
struct Base
{
// other function
// ....
void Func ()
{
typedef typename ::conditional16,primary_t,spec_t>::type type;
Func_impl(type());
}
private:
struct primary_t { };
struct spec_t { };
void Func_impl (primary_t) { std::cout << " primary function " << std::endl; }
void Func_impl (spec_t ) { std::cout << " specialization function " << std::endl; }
};
点评: 和方法6类似,通过函数重载实现
方法8:
namespace
template < bool,typename T = void> struct enable_if { typedef T type; };
template
}
template< class T, unsigned B>
struct Base
{
// other function
// ....
template
typename ::enable_if< 16!=N>::type
FuncImpl () { std::cout << " primary function " << std::endl; }
template
typename ::enable_if< 16==N>::type
FuncImpl () { std::cout << " specialization function " << std::endl; }
void Func() {
FuncImpl();
}
};
点评:通过enable_if, 利用SFINAE实现。
我们可以看到根据编译时模板参数int值的不同,我们重写模板类的某个成员函数的方法是多种多样的。针对上面这种情况,个人其实最推荐方法2,我们没必要把简单的问题复杂化。
下面我们考虑另外一个需求, 当模板类的某个参数是某种类型时, 我们要求特化其中的一个成员函数:
struct Base
{
// other function
// ....
void Func(){ cout << " primary function " << endl; }
};
void test2()
{
Base< int, int> a;
a.Func();
Base< int, string> b;
b.Func();
}
int main()
{
test2();
}
要求上面的模板类如果T2 是string类型, 我们要求对Func特殊重写,其他的成员函数无论什么情况实现都是一样的。
有了上面的那个例子的实现经验, 对这个问题我们解决就方便多了。
方法1:
struct Base
{
// other function
// ....
void Func()
{
if(typeid(std:: string) == typeid(T2))
{
cout<< " specialization function "<
else
{
cout << " primary function " << endl;
}
}
};
点评:通过运行时类型识别(RTTI)实现,需要打开相关编译选项,并且低效。
方法2:
struct Base
{
// other function
// ....
template
void FuncImpl()
{
cout << " primary function " << endl;
}
template<>
void FuncImpl< string>()
{
cout << " specialization function " << endl;
}
void Func()
{
FuncImpl
}
};
点评:通过成员函数特化实现
方法3:
struct Base
{
// other function
// ....
template
class Type2Type
{
typedef T type;
};
template
void FunImpl( const Type2Type
{
cout << " primary function " << endl;
}
template
void FunImpl( const Type2Type< string>)
{
cout << " specialization function " << endl;
}
void Func()
{
FunImpl
}
};
点评: 通过函数重载实现
方法4:
struct IsString
{
enum { value = false };
};
template<>
struct IsString< string>
{
enum { value = true };
};
template
struct Base
{
// other function
// ....
void Func()
{
if(IsString
{
cout << " specialization function " << endl;
}
else
{
cout << " primary function " << endl;
}
}
};
点评: 通过编译时类型判断实现。
方法5:
struct must_be_same_type
{
enum { ret = 0 };
};
template<>
struct must_be_same_type< string, string>
{
enum { ret = 1 };
};
template < typename T1,typename T2 >
class Base{
public:
// other function
// ....
void Func(){
if(must_be_same_type
{
cout << " specialization function " << endl;
}
else
{
cout << " primary function " << endl;
}
}
};
点评: 和方法4类似, 是不过实现方式不一样。
最后,探讨下我自己遇到的问题, 我们在写一个事件委托(delegate)类,大概如下:
class CEvent
{
public:
// other function
// ....
return_type operator()(first_type p1, second_type p2)
{
return_type ret = return_type();
// ...
// ret = invoker(p1, p2);
return ret;
}
};
void test3()
{
CEvent< int, int, int> e1;
e1( 1, 2);
CEvent< void, int, int> e2;
e2( 1, 2);
}
int main()
{
test3();
}
我们可以看到,当return_type是void时, 因为没有返回值,上面的代码会编译失败,因此我们只能偏特化这种情况:
class CEvent< void, first_type, second_type>
{
public:
// other function
// ....
void operator()(first_type p1, second_type p2)
{
// ...
// invoker(p1, p2);
return;
}
};
但是,我们会发现只有这个operator()函数是需要根据return_type特殊对待的,其他函数永远都是一样的。
我们现在的问题就是如何只特化这个函数。
首先我们会想到如下的实现方法:
struct IsVoid
{
enum { value = false };
};
template<>
struct IsVoid< void>
{
enum { value = true };
};
template
class CEvent
{
public:
other function
....
return_type operator()(first_type p1, second_type p2)
{
if(IsVoid
{
cout << " return type is void " << endl;
// ...
// invoker(p1, p2);
}
else
{
cout << " return type is not void " << endl;
return_type ret = return_type();
// ...
// ret = invoker(p1, p2);
return ret;
}
}
};
但是我们很快会发现这种情况下if语句被编译进去了, 所以return_type是void的情况下还是会编译失败。
我们要解决的问题就是如何把这个if语句变成函数重载,于是我们想到如下实现:
struct IsVoid
{
enum { value = false };
};
template<>
struct IsVoid< void>
{
enum { value = true };
};
template< int v>
class Int2Type
{
enum {value = v };
};
template
class CEvent
{
public:
// other function
// ....
return_type InvokerImpl(first_type p1, second_type p2, Int2Type< true>)
{
cout << " return type is void " << endl;
// ...
// invoker(p1, p2);
}
return_type InvokerImpl(first_type p1, second_type p2, Int2Type< false>)
{
cout << " return type is not void " << endl;
return_type ret = return_type();
// ...
// ret = invoker(p1, p2);
return ret;
}
return_type operator()(first_type p1, second_type p2)
{
return InvokerImpl(p1, p2, Int2Type
}
};
上面的实现首先通过编译时类型识别,然后再把识别后相应的bool值转成不同类型, 最后再利用不同类型函数重载实现。
最后总结下,我们可以看到,从编译时到运行时,从面向对象到普通泛型编程再到模板元编程,C++复杂得让人无语, 也强大得让人无语, 而且C++语言本身是在不断发展的(C++11), 同一问题在C++中往往有多种解决方案,这些解决方案有的简单,有的复杂,有的高效, 也有的低效, 而我们的目标就是利用C++这把利器寻找简单而高效的解决方案。
注:本人初学C++ templates编程,如有错误,欢迎指正。
参考资料:http://bbs.csdn.net/topics/390116038
http://bbs.csdn.net/topics/270041821