-1

I would like to now if the following is possible.

I have a templated class called A which inherits from a class called Base. In Base I set a write() function to be rewritten for every derived class.

I am creating a vector to store the references of the Base objects to be printer latter (dataBase).

I would like to know if it is possible to retrieve the reference of the A object whose reference I passed to dataBase.

I have the following code:

#include <iostream>
#include <string>
#include <array>
#include <vector>

class Base
{
    public:
        Base(std::string name):name_(name){}
        virtual ~Base(){}
        virtual void write()=0;

    const std::string& name() const
    {
        return name_;
    }
    
    private:
        std::string name_;
};

template< typename T>
class A : public Base
{
    public:
        A(std::string name):Base(name),name2_(name + "test"){}
        ~A(){}

        void write();

        std::string name2_;

};

template< typename T>
void A<T>::write()
{
    std::cout <<  name2_ << std::endl;
}


int main()
{
        
    A<int> one("one");
    A<double> two("two");
    A<std::array<double,4>> three("three");

    std::vector<Base*> dataBase;
    dataBase.push_back(&one);
    dataBase.push_back(&two);
    dataBase.push_back(&three);

    for(auto i : dataBase)
    {
        i->write();
    }

    A<int>& getOne = lookup("one"); // this is what I want to create
    getOne.name2_  = "worked";

    for(auto i : dataBase)
    {
        i->write();
    }

    return 0;
}

Best Regards

Manuel Oliveira
  • 527
  • 5
  • 19

1 Answers1

1
A<int>& lookup(std::vector<Base*> & dataBase, // need to provide database
               const std::string & seeking)
{
    // find a match
    auto found = std::find_if(dataBase.begin(),
                              dataBase.end(),
                              [seeking](Base * item)
                              {
                                return item->name() == seeking;
                              });
    if (found != dataBase.end())
    { // found it
        // convert to A<int>
        A<int> * temp = dynamic_cast<A<int>*>(*found);
        if (temp) // dynamic_cast returns nullptr on failure.
        { // successful conversion
            return *temp; // return it.
        }
        throw std::runtime_error("wrong type"); // What we found isn't the desired type
    }
    throw std::runtime_error("not found"); // Couldn't find a match
}

Note: when returning a reference, you need to return a reference to a valid object. You can't legally return a nullptr to signal failure, so instead we throw.

Usage:

A<int>& getOne = lookup(dataBase, "one");
getOne.name2_  = "worked";

If you

A<int>& getTwo = lookup(dataBase, "two");
getTwo.name2_  = "worked";

two will be found, but the type will not match and an A<int> & can't be returned. An exception will be thrown.

If you

A<int>& getFoo = lookup(dataBase, "foo");
getFoo.name2_  = "worked";

foo will not be be found and an A<int> & can't be returned. An exception will be thrown.

Note: using a dynamic_cast often means the base class interface is not sufficiently defined to make for a good base class. See the Liskov Substitution Principle for a good test to see whether nor not inheritance is a good choice to use here.

Documentation for std::find_if

Documentation for dynamic_cast

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Hello and thank you very much for the reply. Doing `dynamic_cast_` is a safe approach right? I will just change your function to include a template type (for different types of `A`) – Manuel Oliveira Nov 24 '21 at 20:15
  • @ManuelOliveira `dynamic_cast` is as safe as C++ gets, but it's usually better to not need it at all. – user4581301 Nov 24 '21 at 20:48