1

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);
}
2
  • A map<char*, Derived*> is unrelated to a map<char*, Base*>, and rightly so. Otherwise, you could have the same problems as with Base** and Derived**. Commented Dec 24, 2013 at 13:09
  • 1
    (Side remark: The dynamic allocation is unnecessary here. Consider using just map<char*, Derived*> d;. It's also error-prone to use owning raw pointers inside Standard Library containers.) Commented Dec 24, 2013 at 13:11

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

4 Comments

I'm not sure if polymorphism (as in: different behaviour depending on the dynamic type) is actually necessary here, the OP's call function only uses a member of the base class.
@DyP: It's necessary in order to use dynamic_cast in this manner. Whether or not using dynamic_cast is necessary depends on OP's design.
Umm.. I don't see any necessity for a cast in the OP's code. There's it->second->index, which accesses a member of the base class, and I can't see any accesses to members of derived classes. Of course, when initially using a map<char*,Base*> (with owning Base*), you need polymorphism again.
@DyP: OP's posted code might not reflect the actual use case completely. I don't know that the cast is necesarry; I'm simply offering it up as a possible solution.
1

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?

2 Comments

map<char *, Base *>::iterator it = base->begin(); will not work for a map<..> const&. You'll have to use a const_iterator.
That's what I get for doing in place edits instead of sticking it in the compiler and making sure...doh! Fixed.
1

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.

3 Comments

You may also want to use a shared_ptr or similar to manage the ownership and lifespan of the Derived pointers in your map. Otherwise they'll need to be manually cleaned up later.
@DyP Thanks. I'll fix it up. Serves me right for compiling snippets of code without warnings as errors ;)
Rob, "char*" is my mistake with preparing example, thanks for remark.

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.