Inheritance in std::map with base class as value

大兔子大兔子 提交于 2020-01-06 04:03:38

问题


Here is a code:

class Base {
    public:
        long index;
};
class Derived : public Base {
    public:
        bool value;
};

void call(map<char *, Base *> *base) {
    map<char *, Base *>::iterator it = base->begin();
    cout << it->second->index << endl;
}
void test(void) {
    map<char *, Derived *> *d = new map<char *, Derived *>;

    call(d);
}

Compiler alerts an error:

error C2664: 'call' : cannot convert parameter 1 from 'std::map<_Kty,_Ty> *' to 'std::map<_Kty,_Ty> *'
1>        with
1>        [
1>            _Kty=char *,
1>            _Ty=Derived *
1>        ]
1>        and
1>        [
1>            _Kty=char *,
1>            _Ty=Base *
1>        ]
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

I understand why this error is happened. I do not understand how to make it work. What kind of cast and how to use it?

UPD

I'm sorry for imprecision, let me explain more details. I have two sets of data represented by ClassA and ClassB. Both of these classes have one common member - an "index", for example. Both sets are wrapped into a maps (special thanks to Rob for a significant correction with char*):

std::map<char, ClassA>
std::map<char, ClassB>

Sometime I need to iterate over both maps to get a value of a common member "index". I'm trying to avoid code duplication and make just one function to iterate over both maps.

I thought I may extract a superclass with a common member and make a function with parameter like this one:

std::map<char, SuperClassAB>

or

std::map<char, SuperClassAB>::iterator

But looks like it's a bad idea.

UPD2

One smart guy gave me the solution:

template <class T>
void call(map<char, T> *base) {
    map<char, T>::iterator it = base->begin();
    cout << it->second->index << endl;
}
void test(void) {
    map<char, Derived *> d;
    call(&d);
}

回答1:


No one seemed to suggest this yet, but you could also make call a function template.

    template <class Type>
    void call(const std::map<char*,Type*> & base)
    {
        static_assert(std::is_base_of<Base, Type>::value,
                      "Function is only callable with maps "
                      "whose mapped_type is derived from Base");
        /* More stuff */
    }

This way the function is callable with Base, Derived and anything else that derives from Base.




回答2:


You can't cast these types. map<char*,Base*> and map<char*,Derived*> are as different as string and float.

Simplest thing to do is within test() simply populate a map<char*,Base*> and call call with that. Provide virtual methods in Base (possibly pure virtual), and implement those functions in Derived.

You could attempt to transform a map<char*,Derived*> from a map<char*,Base*>, but in order to do so your functor would need to either:

  1. Absolutely know that the Base* actually points to a Derived object, and use static_cast.
  2. Make Base polymorphic (usually by implementing a virtual destructor), and use dynamic_cast



回答3:


If your goal is to "operate" in some way on the derived class, create a virtual method on the base class and override it to get the specific behavior you want:

class Base
{
public:
   long index;
   virtual void doSomething()
   {
      // Do something with index
      cout << index << endl;
   }
};

class Derived : public Base
{
public:
   bool value;

   virtual void doSomething()
   {
      // Do something with value
      cout << value << endl;
   }
};

// NOTE:  I removed the pointer and made it a ref.
// NOTE:  I made it "const" because you probably don't want to alter the
// map.  If you do...
void call(const map<char *, Base *>& base)
{
   map<char *, Base *>::const_iterator it = base.begin();
   //   cout << it->second->index << endl;
   it->second->doSomething();
}

void test(void)
{
   map<char *, Base *> d;
   // Push some members into d...I didn't, but you should
   // if you want call(...) to be meaningful.
   call(d);
}

Was this helpful?




回答4:


You probably need your container to store base class pointers instead of derived class pointers.

E.g.map<char *, Base *> *d;.

Thereafter, you should populate the map with elements of whatever derived types you want. E.g.

char keyA = 'A';
char keyB = 'B';
(*d)[&keyA] = new Derived();
(*d)[&keyB] = new AnotherDerived();

Would that work in your instance?

As an aside, why are you using char * as the key seems like an odd choice.



来源:https://stackoverflow.com/questions/20761457/inheritance-in-stdmap-with-base-class-as-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!