【问题标题】:Using Templates to resolve virtual methods使用模板解析虚方法
【发布时间】:2014-08-17 19:56:10
【问题描述】:

此问题涉及使用模板来解析 Dispatch 模式中的虚拟成员。
注意:这与 StackOverflow 上已经提出的虚拟模板方法问题不同。 *

编辑 1:更正语法错误,添加说明。

鉴于以下情况:

#include <string>
#include <iostream>

class Field_Interface
{
  public:
    virtual std::string  get_field_name(void) const = 0;
};

class Field_Integer : public Field_Interface
{
  public:
    std::string get_field_name(void) const
    { return "INT";}
};

class Field_String : public Field_Interface
{
  public:
    std::string get_field_name(void) const
    { return "VARCHAR";}
};

class Field_Double : public Field_Interface
{
  public:
    std::string get_field_name(void) const
    { return "DOUBLE";}
};


class Abstract_Visitor
{
  public:
  virtual void visit(const Field_Integer& fi) = 0;
  virtual void visit(const Field_String& fi) = 0;
  virtual void visit(const Field_Double& fi) = 0;
};

class Visitor_Name_Query_1 : public Abstract_Visitor
{
  public:
  template <class Field>
  void visit(const Field& f)
  {
      std::cout << "Field name is: "
                << f.get_field_name()
                << "\n";
  }
};

class Visitor_Name_Query_2 : public Abstract_Visitor
{
  public:
    void visit(const Field_Integer& fi)
    { print_field_name(fi); }

    void visit(const Field_String& fi)
    { print_field_name(fi); }

    void visit(const Field_Double& fi)
    { print_field_name(fi); }

  private:
    void print_field_name(const Field_Interface& fi)
    { 
        std::cout << "Field name is: "
                  << fi.get_field_name()
                  << "\n";
    }
};

int main(void)
{
    Visitor_Name_Query_1    q1;
    Field_Integer           fi;
    q1.visit(f1);
    return 0;
}

编译器说Visitor_Name_Query_1 中的模板化方法没有解析来自Abstract_Visitor 的抽象接口。

编辑 2:g++ 的结果

# g++ -o main.exe main.cpp
main.cpp: In function `int main()':
main.cpp:75: error: cannot declare variable `q1' to be of type `Visitor_Name_Query_1'
main.cpp:75: error:   because the following virtual functions are abstract:
main.cpp:35: error:  virtual void Abstract_Visitor::visit(const Field_Integer&)
main.cpp:36: error:  virtual void Abstract_Visitor::visit(const Field_String&)
main.cpp:37: error:  virtual void Abstract_Visitor::visit(const Field_Double&)
main.cpp:77: error: `f1' undeclared (first use this function)
main.cpp:77: error: (Each undeclared identifier is reported only once for each function it appears in.)

Visitor_Name_Query_1 是一种简化Visitor_Name_Query_2 类的尝试。当visit 方法的数量超过一个简单的数量(如5 个)时,维护变得乏味。这就是template 声明的原因。

扩展模板时,使用其中一种字段类型,声明与Visitor_Name_Query_2 中的声明匹配。

那么为什么编译器生成说class Visitor_Name_Query_1 是抽象的?

注意:我在 Windows Vista 上使用 Visual Studio 2008。

* 其他帖子涉及使用模板来创建虚拟方法声明。我正在使用模板来创建实现抽象方法的函数。

【问题讨论】:

  • 类成员模板不能是虚拟的。
  • 当你说“这不一样”时,你的意思实际上是“这完全一样”吗?
  • 当我修复所有似乎与您的问题完全无关的编译器错误(例如语法错误和缺少继承)时,在 VC 2013 中编译时甚至没有警告。
  • 使用模板。 Abstract_Visitor 是不必要的,所以去掉它。

标签: c++ templates visual-studio-2008 dispatch visitor-pattern


【解决方案1】:

那么为什么编译器生成说类Visitor_Name_Query_1 是抽象的?

因为标准是这样说的。 §14.5.2 [temp.mem]/p4:

成员函数模板的特化不会覆盖 来自基类的虚函数。 [例子

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
};

结束示例 ]

【讨论】:

    【解决方案2】:

    看来您确实希望Abstract_Visitor 具有默认实现。如果将template 移动到Abstract_Visitor 中,则可以让每个virtual 访问者都有默认实现。

    class Abstract_Visitor
    {
      template <class Field>
      void visit(const Field& f)
      {
          std::cout << "Field name is: "
                    << f.get_field_name()
                    << "\n";
      }
      public:
      virtual void visit(const Field_Integer& fi) { visit<>(fi); }
      virtual void visit(const Field_String& fi) { visit<>(fi); }
      virtual void visit(const Field_Double& fi) { visit<>(fi); }
    };
    

    【讨论】:

    • 不,我不是在寻找默认实现。我有很多(如果不是全部)visit 函数以相同方式处理的情况,例如我问题中的打印示例。
    • @ThomasMatthews:感谢您的澄清,但您能解释一下这与默认实现有何不同吗?另外,您觉得这种方法如何阻止您为不同类型的访问者实现不同类型的访问者?
    【解决方案3】:

    由于所有字段类型都有一个通用接口,如果可能,您可以通过更改接口来简化问题:

    class Abstract_Visitor
    {
      public:
      virtual void visit(const Field_Interface& f) = 0;
    };
    
    class Visitor_Name_Query_3 : public Abstract_Visitor
    {
      public:
      void visit(const Field_Interface& f)
      {
          std::cout << "Field name is: "
                    << f.get_field_name()
                    << "\n";
      }
    };
    

    【讨论】:

    • 感谢您的示例,我确实有这样的访问者,其中Field_Interface 访问者捕获了未实现(访问)的字段类型。
    猜你喜欢
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2016-09-28
    • 2011-12-19
    相关资源
    最近更新 更多