【问题标题】:Checking for existence of C++ member function, possibly protected检查 C++ 成员函数是否存在,可能受保护
【发布时间】:2014-03-01 07:49:45
【问题描述】:

我正在尝试检测一个类是否具有特定功能(特别是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. 在 gcc、clang 和 MSVC 2013 下编译(最后一个可能限制过于花哨的 SFINAE)

编辑:我有一个可行的解决方案,但需要与帮助程序类成为朋友,这也不是一个理想的解决方案,但目前可能是一种解决方法(因为它满足所有要求):

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&lt;T&gt;继承给你std::shared_ptr&lt;T&gt; shared_from_this(),而T最终是我需要弄清楚的。这是正确序列化从std::enable_shared_from_this 继承的类型所必需的。

编辑第 3 部分:编辑:

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

【问题讨论】:

  • 这可能有帮助,也可能没有帮助:bloglitb.blogspot.ca/2010/07/…
  • 我有一个通用实现,我可以检查它是否有效,但是,可以肯定的是,我需要一个具有此方法并展示所有要求 (1-4) 的类的原型示例才能工作与。
  • 私有访问黑客的问题在于它需要知道返回类型 - 请参阅struct Af { typedef void(A::*type)(); }; 行。在我描述的情况下,shared_from_this 的返回类型未知。
  • 我添加了一些结构示例以及这种类型特征的预期行为应该是什么。
  • 如果它是最终类并且受到保护,你在乎吗?您无法访问该方法,句号。

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


【解决方案1】:

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

手头的问题很有趣:如何检查一个类是否实现了隐藏接口。不幸的是,这个问题与 liskov 替换原则相矛盾。面向对象的核心原则之一。

这部分是由于std::shared_ptr 的类型结构。 shared_ptr 不反映其参数类型的继承关系。给定一个类T 和一个类U,其中class T : public U {}; 持有shared_ptr&lt;T&gt; : public shared_ptr&lt;U&gt; {}; 没有

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

此外,如果std::shared_ptr 被弃用或者您想使用其他方式来获取内存(std::allocator 接口?一些区域/池分配),您将不得不调整您的接口。

我个人的看法是创建某种工厂接口,并在反序列化器中的某处注册。

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

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 以外的其他东西,他可以做(通过创建自己的 Allocator
  • 用户无需实现任何东西,只需指定类型(并实际将它们传递给您,因此无需再猜测)。

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

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

注意

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

【讨论】:

    【解决方案2】:

    我将冒昧地质疑这个问题。并非每个通过 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!
    

    【讨论】:

    • 检测类型是否为shared_ptr 不是问题,也不是问题所在。这里的困难任务是确定 1) 类是否派生自 enable_shared_from_this&lt;T&gt; 和 2) 找出 T 的真实类型。最好的方法(据我所知)是查看shared_from_this() 的返回类型,它将返回一个shared_ptr&lt;T&gt;,从中可以轻松提取T。它是在我在问题中概述的条件下检测此功能是否存在,这是真正的挑战。
    【解决方案3】:

    如果唯一的目标是检测类型 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

    【讨论】:

    • 最终目标是序列化任何继承自std::enable_shared_from_this 的用户定义类型。要求用户使用这样的特殊包装器是不可行的,即使假设 typedef 是公开可见的,它确实可以轻松解决问题。
    • 我编辑了我的帖子。您说“(特别是 shared_from_this(),它继承自 std::enable_shared_from_this)”所以我认为所有可序列化的类都有一个您可以控制的共同祖先。
    • 否 - 我无法控制用户继承的层次结构。我只想检测它们从std::enable_shared_from_this&lt;T&gt; 继承并最终通过shared_from_this() 函数推断出T 的时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    • 2013-11-17
    • 2012-07-04
    • 2015-11-23
    • 2016-06-12
    相关资源
    最近更新 更多