检查是否存在C ++成员函数,可能受到保护

时间:2014-03-01 07:49:46

标签: c++ c++11 final template-meta-programming sfinae

我正在尝试检测某个类是否具有特定功能(具体为shared_from_this(),它是从std::enable_shared_from_this<Some Unknown Class>继承的)。为了使事情变得更复杂,我需要知道它是否具有此功能,即使它已从远程基类继承或使用受保护的访问继承。

我已经查看了其他问题,例如this one,但提供的方法不适用于检测受保护的成员函数。

我目前使用的方法如下:

template <class T>
struct shared_from_this_wrapper : public T
{
  template <class U>
  static auto check( U const & t ) -> decltype( t.shared_from_this(), std::true_type() );

  static auto check( ... ) -> decltype( std::false_type() );
};

template<class T>
struct has_shared_from_this : decltype(shared_from_this_wrapper<T>::check(std::declval<shared_from_this_wrapper<T>>()))
{ };

我当前解决方案中的缺陷是它不适用于声明为final的类。所以我正在寻找一个满足以下成员函数测试的解决方案:

  1. 使用声明为final
  2. 的类
  3. 使用受保护的成员函数
  4. 使用继承
  5. 不需要知道函数的返回类型
  6. 根据gcc,clang和MSVC 2013进行编译(最后一个可能限制过于花哨的SFINAE)
  7. 编辑:我有一个可行的解决方案,但需要与帮助类成交,这也不是一个理想的解决方案,但现在可能是解决方法(因为它满足所有要求):

    struct access
    {
      template <class T>
      static auto shared_from_this( T const & t ) -> decltype( t.shared_from_this() );
    };
    
    template <class U>
    static auto check( U const & t ) -> decltype( access::shared_from_this(t), std::true_type() );
    
    static auto check( ... ) -> decltype( std::false_type() );
    
    template<class T>
    struct has_shared_from_this2 : decltype(check(std::declval<T>()))
    { };
    
    struct A : std::enable_shared_from_this<A> {};
    struct B : protected A { friend class access; };    
    

    另一个编辑:类的示例以及检查shared_from_this之类存在的类型特征应返回的内容:

    struct A : std::enable_shared_from_this<A> {}; // should return true
    struct B final : protected A {}; // should return true
    struct C : A {}; // should return true
    struct D {}; // should return false
    

    我应该提到我检测这个函数是否存在的最终目的是确定它的返回类型,以便找出std::enable_shared_from_this被模板化的类型。继承std::enable_shared_from_this<T>会为您提供std::shared_ptr<T> shared_from_this(),而T最终是我需要弄清楚的。这对于从std::enable_shared_from_this继承的类型的正确序列化是必需的。

    编辑第3部分:编辑:

    这是为序列化库cereal完成的,因此我无法控制用户如何设计他们的类。我希望能够序列化从std::enable_shared_from_this派生的任何用户类型,其中包括将其类声明为final或在此过程中使用受保护继承的用户。任何需要干预被检查的实际类型的解决方案都不是有效的解决方案。

3 个答案:

答案 0 :(得分:1)

我已经对如何实现你要求的事情提出了一些想法,并得出了完全不同的结论。

手头的问题非常有趣:我如何检查一个类是否实现了一个隐藏的接口。不幸的是,这个问题与利斯科夫替代原则相矛盾;核心面向对象原则之一。

部分原因是std::shared_ptr的类型结构。 shared_ptr不反映其参数类型的继承关系。鉴于课程T和课程U,其中class T : public U {};包含shared_ptr<T> : public shared_ptr<U> {};

您的实现在接口级别存在一个基本缺陷。如果您正在查看编译时是否存在函数然后提取类型,您将只能反序列化使用共享指针的数据结构。

此外,如果{@ 1}}被弃用或您想使用其他方法获取内存(std::shared_ptr接口?某些区域/池分配),则必须调整接口。

我个人的意见是创建某种工厂界面并在解串器中的某处注册。

第二个是拥有一个公开隐式模板接口的工厂类(并使用CRTP来专门化用户需要的接口。即:

std::allocator
  • 这为您的模板类提供了合理的抽象量。
  • 无论如何,您的图书馆用户知道他想要的回报。并且如果他选择分配除template <class ActualType, class IfType=ActualType, class Allocator=default::allocator<ActualType>> class Deserializable { static IfType alloc(ActualType &&t) { Allocator a; // choose another interface as your please. return a.allocate(t); /* implement me */ } private: }; class MyClass : public InterfaceClass, public Deserializable<MyClass,InterfaceClass> { /* your stuff here */ }; 之外的其他东西(通过创建他自己的std::shared_ptr
  • 除了指定类型之外,用户不必实现任何操作(并且实际上将它们传递给您,因此没有其他猜测)。

您可以将此解释为政策类(严格意义上的Andrei Alexandrescu)。 序列化库要求分配策略。用户可以决定如何实施此策略。在这种情况下,选择如何分配反序列化对象类型,这可能是不同的。由于Allocator具有默认实现并且是模板参数,因此如果需要,将向用户传递另一个选项。

为了理解这种方法的强大功能,我欢迎您查看使用此技术的boost::operator代码,以便在编译时指定算术运算符的返回类型和参数。

注意

对于那些也在查看此帖子以获取原始问题答案的人,我建议使用this approach。但是它要求成员是公共的,因为它检查给定名称的成员函数指针。

答案 1 :(得分:0)

我将冒昧地质疑这个问题。并非每个通过shared_ptr传递的对象都继承自enable_shared_from_this。

也许这就是你要找的东西或者给出一些进一步的想法:

class Foo1 { };
class Foo2 : public std::enable_shared_from_this< Foo2 > {};
class Foo3  final : protected Foo2 {};

struct Serialize
{
    template <typename T>
    void write( T* ) {  printf( "not shared!\n" ); }

    template <typename T>
    void write( std::shared_ptr<T> ) { printf( "shared!\n" ); }
};

int test( )
{
    typedef std::shared_ptr<Foo2> Foo2Ptr;
    typedef std::shared_ptr<Foo3> Foo3Ptr;

    Serialize   s;
    Foo1*       pFoo1 = nullptr;
    Foo2Ptr     pFoo2;
    Foo3Ptr     pFoo3;
    s.write( pFoo1 );
    s.write( pFoo2 );
    s.write( pFoo3 );

    return 0;
}

在运行时,输出为:

not shared!
shared!
shared!

答案 2 :(得分:-1)

如果唯一的目标是检测类型T,那么我建议你喜欢在STL中添加一个typedef:

template <class T>
struct enable_shared_from_this : public T
{
    typedef T base_type;
    // ...
};

然后你可以像这样使用它:

class A : enable_shared_from_this<B>
{
}

A::base_type // == B

此示例假定您知道 A继承自shared_from_this_wrapper