Note that there are some explanatory texts on larger screens.

plurals
  1. POGeneric visitor base class template in C++ - overload issue
    text
    copied!<p>I thought it would be a simple exercise to write a generic visitor base class template. The goal is to be able to write</p> <pre><code>typedef visitor&lt;some_base, some_derived1, some_derived2&gt; my_visitor; </code></pre> <p>...and then have my_visitor be a type that is functionally equivalent to</p> <pre><code>struct my_visitor { virtual void visit(some_base&amp;) {} virtual void visit(some_derived1&amp;) {} virtual void visit(some_derived2&amp;) {} }; </code></pre> <p>which i can inherit with actually useful derived visitor classes for that type hierarchy, which override the different visit() versions as needed. I want it to work for any number of types having any inheritance relations, and i don't want to use any hacks that reimplement virtual functions using type_info comparisons. This is what I came up with:</p> <pre><code>#include &lt;cstdlib&gt; #include &lt;iostream&gt; #include &lt;vector&gt; /** This is the generic part that would go in a visitor.hpp header. */ template &lt;typename T&gt; struct visitor_base { virtual void visit(T&amp;) {}; }; template &lt;typename... T&gt; struct visitor : visitor_base&lt;T&gt;... {}; /** This is the part that is specific to a certain type hierarchy. */ struct base; struct derived1; struct derived2; typedef visitor&lt;base, derived1, derived2&gt; my_visitor; /** This is the type hierarchy. */ struct base { virtual void accept(my_visitor&amp; v) { v.visit(*this); } }; struct derived1 : base { derived1() : i(42) {} virtual void accept(my_visitor&amp; v) { v.visit(*this); } int i; }; struct derived2 : base { derived2() : f(2.79) {} virtual void accept(my_visitor&amp; v) { v.visit(*this); } float f; }; /** These are the algorithms. */ struct print_visitor : my_visitor { void visit(base&amp;) { std::cout&lt;&lt;"that was a base."&lt;&lt;std::endl; } void visit(derived1&amp; d) { std::cout&lt;&lt;"that was "&lt;&lt;d.i&lt;&lt;std::endl; } void visit(derived2&amp; d) { std::cout&lt;&lt;"that was "&lt;&lt;d.f&lt;&lt;std::endl; } }; struct randomise_visitor : my_visitor { void visit(derived1&amp; d) { d.i = std::rand(); } void visit(derived2&amp; d) { d.f = std::rand() / float(RAND_MAX); } }; int main() { std::vector&lt;base*&gt; objects { new base, new derived1, new derived2, new derived2, new base }; print_visitor p; randomise_visitor r; for (auto&amp; o : objects) o-&gt;accept(p); for (auto&amp; o : objects) o-&gt;accept(r); for (auto&amp; o : objects) o-&gt;accept(p); } </code></pre> <p>The problem is that this does not compile. GCC says</p> <pre><code>silly_visitor.cpp: In member function ‘virtual void base::accept(my_visitor&amp;)’: silly_visitor.cpp:24:42: error: request for member ‘visit’ is ambiguous silly_visitor.cpp:8:16: error: candidates are: void visitor_base&lt;T&gt;::visit(T&amp;) [with T = derived2] silly_visitor.cpp:8:16: error: void visitor_base&lt;T&gt;::visit(T&amp;) [with T = derived1] silly_visitor.cpp:8:16: error: void visitor_base&lt;T&gt;::visit(T&amp;) [with T = base] </code></pre> <p>Basically, the problem is that since the different visit() member functions are declared in different classes, they are not seen as candidates for overload resolution but just as ambiguous member access. The normal trick for forcing the compiler to consider inherited functions for overload resolution is to redeclare them in the derived class with 'using' statements, but that is not feasible in this case since it would ruin the whole generic aspect of it.</p> <p>So, apparently this was not as easy as I thought. Any ideas?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload