内容简介:C++实现成员函数检查
最近看到一段代码,感觉非常trick,但也非常有意思,写出来记录一下。
背景是这样的,有一个模板函数,其 copy_assign 作用非常简单,就是将第二参数“拷贝”给第一个参数,但是为了对能够进行深拷贝的类型进行深拷贝,希望的行为是这样的:
如果T有成员函数int assign(const T &),则调用dest.assign(src),并以assign函数的返回值作为返回值;
如果T没有成员函数int assign(const T &),则调用dest=src,并返回0。
函数的原型如下:
template < typename T > inline int copy_assign ( T & dest , const T & src ) ;
并且为了降低运行时开销,我们希望这一切是在编译期确定的,所以我们需要在编译期就能够确定类型T是否有assign成员函数,并且根据结果指定对应的行为。
如何判断一个类有没有特定成员函数?
首先要解决的第一个问题是:在模板类的代码部分,我们并不知道类型T是否有我们想要的成员函数,据我所知C++也没有提供这样的机制来判断,那该怎么解决这个问题呢?
我们必须要在编译期利用C++的一些机制让编译器在不报错退出的情况下完成我们的目的,下面定义的模板类就是 __has_assign__ 用来做这件事情的:
template <typename T> struct __has_assign__ { typedef int (T::*Sign)(const T &); typedef char yes[1]; typedef char no[2]; template <typename U, U> struct type_check; template <typename _1> static yes &chk(type_check<Sign, &_1::assign> *); template <typename> static no &chk(...); static bool const value = sizeof(chk<T>(0)) == sizeof(yes); };
代码着实有些trick,需要细细品味。这个模板类针对类型参数T的实例化的静态变量value的值,就代表了类型T中是否有我们想要的assign函数。
代码的关键步骤在函数的最后三行,声明了两个模板函数chk,而第二个函数是总是可以匹配上T的,这就利用了C++模板匹配中的一点规则: 当有多个可行的匹配时,编译器总会选择更“紧”的匹配(更特例化) 。那么什么情况下第一个函数声明会是一个匹配呢?答案在于type_check这个模板类,它接受两个相同的类型参数,而传入的第一个是我们想要的assign函数的类型,第二个参数是模板类型参数的成员函数assign(如果有的话),也就是说,如果传入的类型T是一个具有成员函数 int assign ( const T & ) 的类型,则第一个chk会成为一个更“紧”的匹配被编译器选中,这样静态变量value就会被确定为true,目标达成。
如何判断变量是不是类的实例?
上面这个函数只能对自定义类型使用,那C++的基本类型怎么办呢?我们当然也需要支持基本类型的拷贝。很简单:
template <typename T> struct __has_assign__ { static bool const value = false; };
直接把value设为false就可以了。
如何用一个函数同时适用于类和基本类型?
但上面说的这两个模板类明显是冲突的,不能同时使用,那怎么办呢?
我们可以借助C++模板的偏特例化机制,把这两个模板类通过一个bool类型的参数区分开,形成同一个模板类的两种特例化形式:
template<bool, typename T> struct __has_assign__; template <typename T> struct __has_assign__<true, T> { typedef int (T::*Sign)(const T &); typedef char yes[1]; typedef char no[2]; template <typename U, U> struct type_check; template <typename _1> static yes &chk(type_check<Sign, &_1::assign> *); template <typename> static no &chk(...); static bool const value = sizeof(chk<T>(0)) == sizeof(yes); }; template <typename T> struct __has_assign__<false, T> { static bool const value = false; };
这个模板类怎么用呢?对于一个类型 someClass 来说(可以是基础类型),我们可以通过 __has_assign__ < __is_class ( someClass ) , someClass > :: value 来判断它有没有我们想要的assign函数。 __is_class 是gcc提供的编译器类型判断元语的一个,见 这里 。
实现copy_assign
有了这个神奇的类,我们终于可以实现文章开头提到的 copy_assign 函数了。可是有一个问题,上面代码最后得到了一个表示有没有assign成员函数的bool类型,但是编译期是没有办法使用bool类型的变量值来改变行为的啊?
既然变量不行,那就用类型。
再声明一个模板类,用来把bool类型的变量转变为类型:
template <bool c> struct BoolType { static const bool value = c; }; typedef BoolType<false> FalseType; typedef BoolType<true> TrueType;
该模板类只有一个bool类型的非类型模板参数c,用这个模板类,我们可以把一个bool值转化成对应的类型了!有了不同的类型,再结合C++的重载机制,我们就可以在编译期完成这样的工作了:
template <typename T> inline int copy_assign_wrap(T &dest, const T &src, TrueType c) { return dest.assign(src); } template <typename T> inline int copy_assign_wrap(T &dest, const T &src, FalseType c) { dest = src; return 0; } // 此函数用于拷贝赋值 // - 如果T有成员函数int assign(const T &),则调用dest.assign(src), // 并以assign函数的返回值作为返回值; // - 如果T没有成员函数int assign(const T &),则调用dest=src, // 并返回0。 template <typename T> inline int copy_assign(T &dest, const T &src) { return copy_assign_wrap(dest, src, BoolType<__has_assign__<__is_class(T), T>::value>()); }
针对最后一个参数的类型不同,编译器会在两个重载的模板函数中选择合适的函数实例化,最后达到了我们的目的。
感慨一下
我觉得能写出这样C++代码的人,一定是对C++有着非常深入的了解,真的可以说是 精通 C++了。而我等小菜,连读起来都费劲,只能望其项背啊。
据说C++的元编程是完备的,真的是太神奇了!但另一方面, C++真的是太难学了!
浏览: 0
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 常对象与常成员函数
- C++类中的特殊成员函数
- C++类中的特殊成员函数
- C++成员函数的重载,继承,覆盖和隐藏
- Lisp 家族迎来新成员,函数式语言 Lux 是什么?
- Lisp 家族迎来新成员,函数式语言 Lux 是什么?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Pattern Recognition and Machine Learning
Christopher Bishop / Springer / 2007-10-1 / USD 94.95
The dramatic growth in practical applications for machine learning over the last ten years has been accompanied by many important developments in the underlying algorithms and techniques. For example,......一起来看看 《Pattern Recognition and Machine Learning》 这本书的介绍吧!