代码之家  ›  专栏  ›  技术社区  ›  Thomas Matthews

访问者和模板化虚拟方法

  •  9
  • Thomas Matthews  · 技术社区  · 14 年前

    在一个典型的实现中 访客 模式,类必须考虑到基类的所有变体(后代)。在许多情况下,访问者中相同的方法内容应用于不同的方法。在这种情况下,模板化虚拟方法是理想的,但目前不允许这样做。

    那么,模板化方法可以用来解析父类的虚拟方法吗?

    给予(基金会):

    struct Visitor_Base; // Forward declaration.
    
    struct Base
    {
      virtual accept_visitor(Visitor_Base& visitor) = 0;
    };
    
    // More forward declarations
    struct Base_Int;
    struct Base_Long;
    struct Base_Short;
    struct Base_UInt;
    struct Base_ULong;
    struct Base_UShort;
    
    struct Visitor_Base
    {
      virtual void operator()(Base_Int& b) = 0;
      virtual void operator()(Base_Long& b) = 0;
      virtual void operator()(Base_Short& b) = 0;
      virtual void operator()(Base_UInt& b) = 0;
      virtual void operator()(Base_ULong& b) = 0;
      virtual void operator()(Base_UShort& b) = 0;
    };
    
    struct Base_Int : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    
    struct Base_Long : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    
    struct Base_Short : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    
    struct Base_UInt : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    
    struct Base_ULong : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    
    struct Base_UShort : public Base
    {
      void accept_visitor(Visitor_Base& visitor)
      {
         visitor(*this);
      }
    };
    

    既然地基已经铺设好了,这里就是踢球者进来的地方(模板方法):

    struct Visitor_Cout : public Visitor_Base
    {
      template <class Receiver>
      void operator() (Receiver& r)
      {
         std::cout << "Visitor_Cout method not implemented.\n";
      }
    };
    

    故意, Visitor_Cout 不包含关键字 virtual 在方法声明中。方法签名的所有其他属性都与父声明匹配(或者 规范 )

    在大图中,这种设计允许开发人员实现常见的访问功能,这些功能只因目标对象(接收访问的对象)的类型不同而不同。上面的实现是我对于当派生的访问者实现没有实现可选方法时发出警报的建议。

    这是合法的C++规范吗?

    (我不相信有人说它与编译器一起工作 XXX . 这是一个反对通用语言的问题。)

    2 回复  |  直到 8 年前
        1
  •  6
  •   Edward Strange    14 年前

    哦,我知道你在找什么。尝试如下操作:

    
    
    template < typename Impl >
    struct Funky_Visitor_Base : Visitor_Base
    {
      // err...
      virtual void operator()(Base_Int& b) { Impl::apply(b) }
      virtual void operator()(Base_Long& b) { Impl::apply(b) }
      virtual void operator()(Base_Short& b) { Impl::apply(b) }
      virtual void operator()(Base_UInt& b) { Impl::apply(b) }
      virtual void operator()(Base_ULong& b) { Impl::apply(b) }
    
      // this actually needs to be like so:
      virtual void operator()(Base_UShort& b)
      {
        static_cast<impl *const>(this)->apply(b) 
      }
    };
    
    struct weird_visitor : Funky_Visitor_Base<weird_visitor>
    {
      // Omit this if you want the compiler to throw a fit instead of runtime error.
      template < typename T >
      void apply(T & t)
      {
        std::cout << "not implemented.";
      }
    
      void apply(Base_UInt & b) { std::cout << "Look what I can do!"; }
    };
    

    也就是说,您应该研究非循环访问者模式。它误解了框架中内置的访问者,因此您不必为永远不会调用的内容实现函数。

    有趣的是,我实际上使用了与此非常相似的东西来为一个类型列表构建一个非循环的访问者。我应用了一个元函数,它基本上构建了一个时髦的访问者库,并将一个操作符(类似于我展示的apply()的东西)转换为完整列表的访问者。这些对象是反射的,因此apply()本身实际上是一个元函数,它基于其命中的任何类型构建。其实很酷很奇怪。

        2
  •  2
  •   James McNellis    14 年前

    在派生的访问者类中, Visitor_Cout , the operator() 模板不重写 运算符() Visitor_Base . 按照C++ 03标准(145.2/4):

    成员函数模板的专门化不会重写基类中的虚拟函数。例:

    class B {
        virtual void f(int);
    };
    
    class D : public B {
        template <class T> void f(T);  // does not override B::f(int)
        void f(int i) { f<>(i); }      // overriding function that calls
                                       // the template instantiation
    };
    

    结束示例

    推荐文章