-2

This issue involves using templates to resolve virtual members in a Dispatch pattern.
Note: This is not the same as virtual template method questions already asked on StackOverflow. *

Edit 1: Corrected syntax errors, added clarifications.

Given the following:

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

The compiler is saying the templated method in Visitor_Name_Query_1 is not resolving the abstract interface from Abstract_Visitor.

Edit 2: Results from 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 is an attempt to simplify the class Visitor_Name_Query_2. When the number of visit methods grows beyond a simple quantity (like 5), maintenance becomes tedious. This is the reason for the template declaration.

When the template is expanded, with one of the field types, the declaration matches the one in Visitor_Name_Query_2.

So why is the compiler generating saying that class Visitor_Name_Query_1 is abstract?

Note: I am using Visual Studio 2008 on Windows Vista.

* The other posts involve using templates to create virtual method declarations. I'm using templates to create functions that implement the abstract methods.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • A class member template can not be virtual. – Constructor Aug 17 '14 at 20:00
  • 5
    When you say "This is not the same as", do you in fact mean "this is exactly the same as"? – Kerrek SB Aug 17 '14 at 20:00
  • When I fix all the compiler errors which seem completely unrelated to your question (e.g. syntax errors and missing inheritance), then this compiles without even a warning in VC 2013. – Christian Hackl Aug 17 '14 at 20:02
  • 1
    @ChristianHackl [It doesn't compile with `Visitor_Name_Query_1` class instantiated](http://rextester.com/EXHRJ22696), of course. – Constructor Aug 17 '14 at 20:24
  • Use the template. The `Abstract_Visitor` is unnecessary, so get rid of it. – jxh Aug 17 '14 at 20:25
  • @Constructor: You are right. Well, I figure I should just have given up after the third syntax-error correction :) – Christian Hackl Aug 17 '14 at 20:34
  • @Constructor: Which template member is virtual? – Thomas Matthews Aug 17 '14 at 21:14
  • @jxh - The `Abstract_Visitor` defines an interface that is used in this example and other examples; so it is needed. – Thomas Matthews Aug 17 '14 at 21:15
  • @KerrekSB: I'm not using a template to define the virtual methods. I am using a template as short-hand notation to generate all the functions that will resolve the interface. – Thomas Matthews Aug 17 '14 at 21:19
  • @ThomasMatthews You assume that specializations of `template void visit(const Field& f)` class member template override corresponded overloads of the virtual method `visit` of the `Abstract_Visitor` base class, don't you? Then the specializations of the `visit` class member template should be virtual... – Constructor Aug 17 '14 at 21:50

3 Answers3

2

So why is the compiler generating saying that class Visitor_Name_Query_1 is abstract?

Because the standard says so. §14.5.2 [temp.mem]/p4:

A specialization of a member function template does not override a virtual function from a base class. [ Example:

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

end example ]

T.C.
  • 133,968
  • 17
  • 288
  • 421
0

It seems you really want the Abstract_Visitor to have a default implementation. If you move the template into the Abstract_Visitor, you can let each virtual visitor have the default implementation.

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); }
};
jxh
  • 69,070
  • 8
  • 110
  • 193
  • No, I'm not looking for a default implementation. I have cases where many, if not all, the `visit` functions are processed in the same way, such as the printing example in my question. – Thomas Matthews Aug 18 '14 at 14:51
  • @ThomasMatthews: Thanks for the clarification, but can you explain how that is different from a default implementation? Also, how do you feel this approach prevents you from implementing a different kind of visitor for a different kind of type? – jxh Aug 18 '14 at 15:18
0

As all field types have a common interface, you could simplify the problem by changing the interface if it is possible:

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";
  }
};
André Sassi
  • 1,076
  • 10
  • 15
  • Thank you for the example, I do have visitors like this, where the `Field_Interface` visitor catches the field types that are not implemented (visited). – Thomas Matthews Aug 18 '14 at 14:53