模板的递归实例化可以分为类模板和函数模板两种。关键是形成依赖,基本内容是编译期类型和常整型值的计算(或者借助于模板函数类型推导)。
类模板
对于类模板来说,使用自身的模板特化(模板参数递进)的成员而形成依赖,导致模板类递归实例化,需要用类模板的特化来指定递归终点。
例子(编译期判断素数及打印):
#include <iostream>
using namespace std;
template< int N, int P, bool Flag >
struct Check;
template< int N >
struct IsPrime
{
// Check<N/2,N,true>::result 的结果就是是否为素数
static const bool value = Check<N/2,N,true>::result;
};
template<>
struct IsPrime<1>
{
static const bool value = false;
};
template<>
struct IsPrime<2>
{
static const bool value = true;
};
// Flag用作递归状态传入
template< int N, int P, bool Flag >
struct Check
{
private:
static const bool tmp =
IsPrime<N>::value ? (P%N != 0)&&Flag : Flag;
public:
static const bool result = tmp ? Check<N-1,P,tmp>::result : false;
};
template< int P, bool Flag >
struct Check<1, P, Flag>
{
static const bool result = Flag;
};
// 是否是素数的信息IsPrime<N>::value --> true or false
template < int N > // 打印小于N的素数
struct PrintPrimes
{
void operator () ()
{
PrintPrimes<N-1>()();
Print< IsPrime<N>::value, N >::pt();
}
};
template<>
struct PrintPrimes<2>
{
void operator()()
{
cout << 2 << endl;
}
};
// 被PrintPrimes使用(选择素数打印)
template<bool flag, int p>
struct Print
{
static void pt()
{
cout << p << endl;
}
};
template<int p>
struct Print<false,p>
{
static void pt() { }
};
int main()
{
PrintPrimes<150>()();
return 0;
}
函数模板
对于函数模板来说,也是因为在函数中调用了同名模板函数(但是模板参数不同),也形成递归实例化,递归重点的设置,是写一个重载的函数即可(因为我们知道终点的特点,它能比模板函数匹配的更好)。
例子(类型安全的format函数):
像printf这样的格式化器,依赖字符串中s%
等表示数据类型,变长参数并不类型安全。下面介绍一种类型安全的格式化器的构造思路。
类型安全的多参数的基本想法:
- 要类型安全,必须使用函数重载,指定类型,不能使用
...
- 要尽量泛型,可以使用模板函数的重载
- 由于仅仅是参数数目的不同,重复代码很多,考虑用tuple维持多参数列表,且使用预处理器preprocessor的文件迭代简化代码。
// 头文件 format.h
/*
format(格式化字符串函数):
1、由于编译期类型识别,格式化字符串format_str不用再加上类似于(printf)的%d来表示int的方法,只需要一个'%'来表示该位置需要被替换
2、格式format( result, format_str, arg1,...,argN )
-> 要求输出目标result是basic_string<T>的类型
-> argn为可输出的参数,要求format_str的'%'数量和N的大小一致
-> 采用预处理元编程,文件迭代,预处理期生成代码, 最多支持的参数由FORMAT_LIMIT可配置
例:
wstring res;
format( res, L"% yes % no %", 100, wstring(L"sjw"), 'd' );
*/
#ifndef BOOST_PP_IS_ITERATING
#ifndef FORMAT_H
#define FORMAT_H
#define FORMAT_LIMIT 10
#include <sstream>
#include <string>
#include <assert.h>
#include <boost/tuple/tuple.hpp>
#include <boost/preprocessor.hpp>
namespace detail_impl {
template<typename CH>
void format_impl( std::basic_string<CH>& resu_str, const CH* s )
{
while ( s && *s )
{
assert( *s != CH('%') ); // missing arguments
resu_str.push_back( *s++ );
}
}
template<typename CH>
void format_impl( std::basic_string<CH>& resu_str, const CH* s, const boost::tuples::null_type& )
{
format_impl( resu_str, s );
}
// 递归实例化的模板函数
template< typename CH, typename Tuple >
void format_impl( std::basic_string<CH>& resu_str, const CH* s, Tuple const& tup )
{
while( s && *s )
{
if ( *s == CH('%') )
{
std::basic_ostringstream< CH> os;
os << tup.get_head();
resu_str += os.str();
// tup.get_tail()产生新的Tuple,导致该实例化新的函数
return format_impl( resu_str, ++s, tup.get_tail() );
}
resu_str.push_back( *s++ );
}
assert( 0 == boost::tuples::length<Tuple>::value ); // too many arguments
}
} // detail_impl
// 不带%格式符
template<typename CH>
static void format( std::basic_string<CH>& resu_str, const CH* format_str )
{
resu_str.clear();
detail_impl::format_impl( resu_str, format_str );
}
// self iterator direction
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, FORMAT_LIMIT, "format.h"))
#include BOOST_PP_ITERATE()
#endif// FORMAT_H
#else // self file iterator
#define n BOOST_PP_ITERATION()
template< typename CH, BOOST_PP_ENUM_PARAMS(n, typename ARG) >
static void format( std::basic_string<CH>& resu_str, const CH* format_str,
BOOST_PP_ENUM_BINARY_PARAMS( n, ARG, const& arg ) )
{
resu_str.clear();
boost::tuple<BOOST_PP_ENUM_PARAMS(n, ARG)> tup( BOOST_PP_ENUM_PARAMS(n, arg) );
detail_impl::format_impl( resu_str, format_str, tup );
}
#endif// iterator end
// 源文件
/*
类型安全的格式化器:
1、借助函数模板递归实现tuple的逐个取用
2、借助预处理元编程减少编写的代码量,借助preprocessor预处理元编程引擎可以很方便的书写,而且有很好的使用文档
3、借助ostringstream可扩展的进行格式化
*/
#include "format.h"
#include <iostream>
using namespace std;
struct Date
{
int year;
int month;
int day;
Date( int y, int m, int d ) :
year(y), month(m), day(d)
{}
};
ostream& operator << ( ostream& out, Date const& date )
{
out << date.year << "-" << date.month << "-" << date.day;
return out;
}
int main()
{
string res;
format( res, "test % yeah~%Yes%No % % % % % %", 12, string("zhenshan"), '%', 1, '2', 3, 4, 5, Date(2010,12,25) );
cout << res << endl;
return 0;
}