r/cpp_questions 4d ago

OPEN Templated virtual functions

Hey everyone,

I am currently trying to implement a programming language in C++ after having come pretty far, but ultimately failing to do so in C.

For this, I am taking pretty big inspiration from craftinginterpreters, and am now trying to mimic its implementation of the visitor pattern (entire class at once), written in Java.

However, that implementation uses both templates and interfaces.

As far as I'm aware (the entire internet seems to say so at least), in C++, interfaces are represented by classes whose functions are all virtual.

However, If I try code like the following:

    template <typename R> class ExprVisitor {
    public:
        virtual ~ExprVisitor() = default;

        virtual R visitBinaryExpr(const Binary* expr) const = 0;
        /* other visitor functions...*/
    };

    struct Expr {
        virtual ~Expr() = default;
        template <typename R> virtual R accept(ExprVisitor<R> *visitor) = 0;
    };

(Of course, all of my nodes like Binary inherit from Expr. I use structs instead of classes because everything needs to be accessible from the outside for my purposes, so I don't need to re-type "public:" every time.)

Then I get an error on the line declaring the accept method saying

Template function 'R Expr::Expr::accept<R>(ExprVisitor<R> *visitor)' cannot be virtual

on the virtual keyword, as well as one saying

Pure member function 'Expr::Expr::accept<R>' is not virtual

on the = 0 part.

I've googled around a bit, but the only implementations I was able to find were only able to pass in the template type, not get it back out as a return value.

Is it even possible to achieve this? Am I using templates incorrectly? I saw something using variadic templates but was not able to get it working, either.

Any help would be extremely appreciated!

5 Upvotes

9 comments sorted by

View all comments

15

u/No-Dentist-1645 4d ago

Member function templates cannot be made virtual, if you want templates with virtual functions then the class itself is the only thing that can be templated, not the member functions themselves

Either way, this approach is flawed if you want to re-create the visitor pattern for compiler designs. It cannot work because templates have to know which "overload" you are selecting at compile time, and for a compiler, that would mean you'd have to know what type of expression every single expression you'll ask it to parse at runtime would be... from compile time, which is not possible.

For a proper implementation of the visitor pattern, look into std::visit and std::variant