2

I understand the implementation of the solution and the double dispatch/visitor pattern, however I don't understand what happens at compile time and run-time that we need this pattern.

For example this code:

#include <iostream>

class A {
public:
};
class B : public A {
};

class F {
public:
    virtual inline void operator()(const A&) const noexcept {
        std::cout << "FxA" << std::endl;
    }
    virtual inline void operator()(const B&) const noexcept {
        std::cout << "FxB" << std::endl;
    }
};

class G : public F {
public:
    virtual inline void operator()(const A&) const noexcept {
        std::cout << "GxA" << std::endl;
    }
    virtual inline void operator()(const B&) const noexcept {
        std::cout << "GxB" << std::endl;
    }
};

void by_const_ref(const F& f, const A& a) {
    f(a);
}

int main() {
    by_const_ref(F(), A());
    by_const_ref(F(), B());
    by_const_ref(G(), A());
    by_const_ref(G(), B());

    return 0;
}

Without double dispatch the second and fourth call to by_const_ref will not resolve B() into a A object.

From this article: https://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_in_C++ I understand that it involves name mangling and compile time and vtable resolution at run-time, but I didn't find how exactly.

For the name-mangling part I looked at the compiled object but didn't find anything special.

For the vtable I dumped it with g++ -fdump-lang-class and it didn't looked like there were much information there too.

Hence my request. I'd like to understand what exactly happened and maybe how to check this behavior (using tools like nm, checking the vtable, the machine code ?)

JayZ
  • 244
  • 1
  • 7
  • 1
    @DanielLangr in this case it's redundant as member function defined directly inside their class are implicitly `inline` already. – Quentin Sep 27 '19 at 13:14
  • @Quentin That was my point. OP may not know that. I deleted my original comment by mistake. Asked about why are member functions `inline`. – Daniel Langr Sep 27 '19 at 13:15

1 Answers1

4

You went too deep, it's way simpler.

C++ virtual functions enable dynamic dispatching based on the type of a single parameter, this. Double dispatching, as the name implies, is dynamic dispatching based on the type of two parameters. Since the language does not provide this feature, the Visitor pattern just uses the built-in single dispatching twice, using each dynamic parameter as this in turn.

You could conceivably implement a Visitor performing triple dispatching or more by continuing this game of musical chairs until all of the dynamic parameters have been this once and been dispatched correctly before the final call.

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • So if I understand correctly. In `by_const_ref`, `f(a)` is in fact: `F::operator()(F* const this, const A&)`. Since the method is virtual the vtable and the vptr will be used to fetch the correct implementation address. Which translate in something like: `ThisVptr->operator()(F* const this, const A&)` with `ThisVptr` pointing to the vtable of the G class for G object. By signature we end up with a `const A&` object as second parameter and so call the matching function. – JayZ Sep 27 '19 at 14:53
  • @JayZ you're mixing in implementation details (such as a vtable), but that's the gist of it, yes. Note that resolving the overload with respect to the `const A&` parameter happens strictly at compile time, which is why passing an `A` of dynamic type `B` does *not* resolve to the `const B&` overload -- it is *statically* dispatched. – Quentin Sep 27 '19 at 14:58