1

I see this question has been discussed in various places, e.g. here,here,here and here .But, i have still not been able to relate to the questions aforementioned.

My situation: I am trying to implement a simple generic visitor pattern in C++. The hosts in this pattern are different pets, these are in a file called Pet.h. The visitors are in a separate header called Visitors.h.

The concrete Pet classes accept, generic visitor classes(See Comment 0 in Pet.h), and the generic visitor classes visits the generic Pet classes. So, there is a natural cyclic dependency.

Earlier, when all the code was in a single header, there were no problems. Now there is. To illustrate, here is the Pet.h class. P.S: I am using Visual Studio 2017.

#pragma once

#include <string>
#include "Visitors.h"


namespace pet
{
    class Pet {
    public:
        virtual ~Pet() {}
        virtual void accept(temp_visitor::PetVisitor& v) = 0; //Comment 0 Pet accepts a gneric PetVisitor
        virtual std::string getAnimal() = 0;
    private:
        std::string color_;
    };

    template <typename Derived>
    class Visitable : public Pet {
    public:
        using Pet::Pet;
        Visitable() = default;
        Visitable(const std::string& animal) : animal_(animal) {}
        void accept(temp_visitor::PetVisitor& v) override {
            v.visit(static_cast<Derived*>(this));  
        }

        std::string getAnimal() override
        {
            return animal_;
        }

        std::string animal_;
    };

    class Cat : public Visitable<Cat> {
        using Visitable<Cat>::Visitable;
    };

    class Dog : public Visitable<Dog> {
        using Visitable<Dog>::Visitable;
    };

}

And here is the Visitors.h file. The void visit(pet::Pet* p) gives the following error "use of undefined type pet::Pet on using p->getAnimal()

#include<iostream>

#pragma once

namespace pet
{
    class Pet; //Comment 1. Attempted forward declaration.
}

namespace temp_visitor
{
    template <typename ... Types>
    class Visitor;

    template <typename T>
    class Visitor<T> {
    public:
        virtual void visit(T* t) = 0;
    };

    using PetVisitor = Visitor<pet::Pet>;

    class FeedingVisitor : public PetVisitor {
    public:
        void visit(pet::Pet* p) override { std::cout << "Feed veggies to the "+ p->getAnimal() << std::endl; }  //Comment 2: Gives the following error "use of undefined type pet::Pet"
    };
}

So how do i fix this problem ?

4
  • You need to move the FeedingVisitor to a new header and cpp as well. In header you will have #include "Visitors.h", forward declration for Pet and in cpp #include "Pet.h" Commented Oct 26, 2022 at 5:58
  • It's so obvious. You cannot forward declare templates. If you manage to do, potentially there would be code duplication in the implementation files. The sole purpose of templates is to avoid code duplication and in my sense, the only way to do that is to define templates in header files. Commented Oct 26, 2022 at 5:59
  • If you use a separate header for each class would think your cyclic dependency will be much easier to solve Commented Oct 26, 2022 at 6:03
  • No, but that is a requirement. Commented Oct 26, 2022 at 6:04

1 Answer 1

1

You need to move the FeedingVisitor to a new header and cpp as well. In header you will have #include "Visitors.h", forward declration for Pet and in cpp #include "Pet.h"

Something like Visitors.hpp

namespace pet  {
    class Pet; //Comment 1. Attempted forward declaration.
}

namespace temp_visitor 
{
    template <typename ... Types>   class Visitor;

    template <typename T>  class Visitor<T> 
    {
    public:
        virtual void visit(T* t) = 0;
    };
}

FeedingVisitor.hpp

#include "Visitors.h"

namespace temp_visitor
{
    using PetVisitor = Visitor<pet::Pet>;

    class FeedingVisitor : public PetVisitor {
    public:
        void visit(pet::Pet* p) override;  // only declaration
    };
}

FeedingVisitor.cpp

// move the definition to cpp
void temp_visitor::FeedingVisitor::visit(pet::Pet* p) { 
   std::cout << "Feed veggies to the " + p->getAnimal() << std::endl; 
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.