检查类是否具有给定签名的成员函数

时间:2020-03-05 18:59:58  来源:igfitidea点击:

我要一个模板技巧来检测一个类是否具有给定签名的特定成员函数。

问题类似于此处引用的问题
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运算符<<,然后根据情况生成不同的代码。

我不认为在找到链接的解决方案之前是不可能的,但这是一个非常巧妙的技巧。花时间了解代码,这是非常值得的。

布拉德