检查类是否具有给定签名的成员函数
我要一个模板技巧来检测一个类是否具有给定签名的特定成员函数。
问题类似于此处引用的问题
http://www.gotw.ca/gotw/071.htm
但不尽相同:在Sutter的书中,他回答了一个问题,即C类必须提供具有特定签名的成员函数,否则该程序将无法编译。在我的问题中,如果一个类具有该功能,则需要执行某些操作,否则执行"其他操作"。
boost :: serialization面临着类似的问题,但是我不喜欢它们采用的解决方案:模板函数默认情况下调用具有特定签名的自由函数(必须定义),除非我们定义特定的成员函数(在他们的情况下,"序列化"使用两个具有给定类型的参数(带有特定签名),否则会发生编译错误。那就是要同时实现介入式和非介入式序列化。
我不喜欢该解决方案,原因有两个:
- 为了避免干扰,我们必须覆盖boost :: serialization命名空间中的全局"序列化"功能,因此我们可以在客户端代码中打开命名空间boost和命名空间序列化!
- 解决该混乱的堆栈是10到12个函数调用。
我需要为没有该成员函数的类定义一个自定义行为,并且我的实体位于不同的名称空间中(并且我不想覆盖在一个名称空间中定义的全局函数,而在另一个名称空间中)
你能给我一个解决这个难题的提示吗?
解决方案
回答
为了避免干扰,我们还可以在要序列化的类或者归档类的名称空间中添加" serialize",这要归功于Koenig查找。有关更多详细信息,请参见自由功能覆盖的命名空间。 :-)
打开任何给定的命名空间以实现自由功能都是错误的。 (例如,我们不应为自己的类型打开名称空间" std"来实现" swap",而应使用Koenig查找。)
回答
好的。第二次尝试。也可以,如果我们也不喜欢这个,我正在寻找更多的想法。
赫伯·萨特(Herb Sutter)的文章讨论了特质。因此,我们可以有一个traits类,其默认实例具有回退行为,对于成员函数所在的每个类,traits类专门用于调用成员函数。我相信Herb的文章提到了一种执行此操作的技术,因此它不会涉及大量复制和粘贴。
但是,就像我说的那样,也许我们不想要与实现该成员的"标记"类有关的额外工作。在这种情况下,我正在寻找第三个解决方案。
回答
我不确定我是否理解正确,但是我们可能会利用SFINAE在编译时检测函数是否存在。我的代码中的示例(测试类是否具有成员函数size_t used_memory()const)。
template<typename T> struct HasUsedMemoryMethod { template<typename U, size_t (U::*)() const> struct SFINAE {}; template<typename U> static char Test(SFINAE<U, &U::used_memory>*); template<typename U> static int Test(...); static const bool Has = sizeof(Test<T>(0)) == sizeof(char); }; template<typename TMap> void ReportMemUsage(const TMap& m, std::true_type) { // We may call used_memory() on m here. } template<typename TMap> void ReportMemUsage(const TMap&, std::false_type) { } template<typename TMap> void ReportMemUsage(const TMap& m) { ReportMemUsage(m, std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>()); }
回答
如果我们知道所需的成员函数的名称,这应该就足够了。 (在这种情况下,如果没有成员函数,则函数bla不能实例化(编写一个仍然有效的函数很困难,因为缺少函数的部分专业化。我们可能需要使用类模板) (类似于enable_if)),也可以根据我们希望其作为成员的功能类型进行模板化。
template <typename T, int (T::*) ()> struct enable { typedef T type; }; template <typename T> typename enable<T, &T::i>::type bla (T&); struct A { void i(); }; struct B { int i(); }; int main() { A a; B b; bla(b); bla(a); }
回答
我相信我们正在寻找的答案就在这里。
http://www.martinecker.com/wiki/index.php?title=检测时间
还有一个稍微填写的示例
http://pastie.org/298994
我使用该技术来检测有关类上是否存在支持的ostream运算符<<,然后根据情况生成不同的代码。
我不认为在找到链接的解决方案之前是不可能的,但这是一个非常巧妙的技巧。花时间了解代码,这是非常值得的。
布拉德