Chinaunix首页 | 论坛 | 博客
  • 博客访问: 602751
  • 博文数量: 149
  • 博客积分: 7191
  • 博客等级: 少将
  • 技术积分: 1561
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-19 14:15
文章分类

全部博文(149)

文章存档

2013年(1)

2011年(2)

2010年(14)

2009年(29)

2008年(26)

2007年(31)

2006年(32)

2005年(14)

分类: C/C++

2005-12-31 10:51:02

疯狂C++模板
原著:Aliaksei Sanko  2001年10月29日


本文运行环境:VC6 SP5, NT4 SP3, Windows 98, Windows 2000

www.cuj.com上有一篇十分有意思关于基于模板技术编程的文章。它用一个简单的struct来实现将哑元转换值转化为可识别类型:

template
struct Int2Type
{
   enum { value = v };
};

文章主要是介绍类,但也指出了为什么模板函数在VC众特别有用。Visual C++ 6.0编译器中存在一个编号为Q240871的bug,在MSDN中的介绍如下:

症状

如果所有的模板参数在函数参数或返回类型中都没有被使用,那么重载的模板函数将不正确。

原因

这个bug是由于编译器修饰模板函数的名字造成的。名字修饰使用参数和返回类型来进行,而并不明确指出模板参数类型。于是,所有这样的模板函数将得到同样的修饰名字<…>

解决

在函数中使用哑参数。

另一篇比较有趣的技术文章是。模板并不处理对象集成,但哑参数则能够在特殊的类模板和它们的子类重实现。我们来举一个应用这些原则实现的指针验证程序的例子。

MFC拥有两个验证宏;ASSERT_VALID用于基于CObject的类,而ASSERT_POINTER用于其他类。我们来介绍一个单一模板函数来取代这二者。由于整个东西都完全可以运行,因此我们省略一些不必要的细节来节省篇幅。ASSERT_VALID宏调用AfxAssertValidObject()。在其他对象中,他检查对象的虚表(virtual table)指针。为了做到这一点,我们首先必须检查类是否拥有它。下面的附加struct可以帮助我们:

template struct HasVirtualTable
{
   class X : public T
   {
      X();
      virtual void dummy();
   };
   enum { has_table = sizeof(X) == sizeof(T) };
};

然后,我们引入一个AssertVTable模板函数,包括“真正的”和它的特殊的void“伪”Int2Type模板参数:

template inline void AssertVTable(const void* pData, T)
{
  ASSERT(AfxIsValidAddress(*(void**)pData, sizeof(void*), FALSE));
}
inline void AssertVTable(const void*, Int2Type<0>) {}

下面是公共的验证程序,在参数表后面包括省略号:

template inline void AssertValidPointer(const T* pData, ...)
{
  ASSERT(AfxIsValidAddress(pData, sizeof(T)));
  AssertVTable(pData,
               Int2Type::has_table>());
}

下面的验证程序针对CObject子类替代上面的那个:

template inline void AssertValidPointer(const T* pOb,
                                                 const CObject*)
{
  ASSERT(AfxIsValidAddress(pOb, sizeof(T)));
  ASSERT(AfxIsValidAddress(*(void**)pOb, sizeof(void*), FALSE));
  pOb->AssertValid();
}

当然,也可以用同样的方式针对其他类的继承类加入验证程序。

下面是应用程序中使用的顶级函数:

template inline void CHECK_ADDRESS(const T* pData)
{
  AssertValidPointer(pData, pData);
}
template inline void CHECK_NULL_OR_ADDRESS(const T* pData)
{
  if(NULL != pData)
     AssertValidPointer(pData, pData);
}

把上面的东西放到#if/#else/#endif块中,以便在发行版(release build)中禁用它们:

#ifdef _DEBUG
   ...
#else //_DEBUG
#define CHECK_ADDRESS(pData)          ((void)0)
#define CHECK_NULL_OR_ADDRESS(pData)  ((void)0)
#endif //!_DEBUG

现在我们就可以使用CHECK_ADDRESS()和CHECK_NULL_OR_ADDRESS()了。

在文章的最后,我斗胆提议使用一个独特的方法来用模板模拟枚举(enums)。这样写只是为了好玩,不过它确实能在无法将枚举项不能放到一起的时候帮助我们:

#include 
#define ENUM(val) 
template<> struct cnt<__LINE__> { enum {v = cnt<__LINE__-1>::v+1}; }; 
enum { val = cnt<__LINE__>::v };   
template<> struct flg { enum {v = 0}; };
namespace test
{
  template struct flg { enum {v = -1}; };
  template struct cnt { enum {v = cnt::v }; };
  template<> struct cnt<__LINE__> { enum {v = -1 }; };

ENUM(a)
// 在这里放一点东西
 ENUM(b)
// 在这里放一点东西
 ENUM(c)

template struct bnd
  {
     enum
     {
        u = bnd<(i+1) | flg::v>::v,
        v = (-1 == u)? i : u
     };
  };
  template<> struct bnd<-1> { enum { v = -1 }; };
  enum { upper_bound = (bnd<0>::v | flg<0>::v)+1 };
};
int main()
{
  cout << test::a << ’,’
       << test::b << ’,’
       << test::c << ’,’
       << test::upper_bound << "
";
  return 0;
}
阅读(1615) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~