0

I'm trying to make a function that reads a polymorphic dynamic array and prints a different thing for each class. The code is something like this:

class A {};
class B: public A {};
class C: public A {};

void function (const A &a) {
    if(B *b = dynamic_cast<B*>(a)){
        cout << "B" << endl;
    }
    if(C *c = dynamic_cast<C*>(a)){
        cout << "C" << endl;
    }
}

int main () {
    A **array = new A* [2];
    array [0] = new B;
    array [1] = new C;

    function (array [0]); // To print B
    function (array [1]); // To print C
}

But it gives me an error that says:

cannot dynamic_cast ‘a’ (of type ‘const class A’) to type ‘class B*’ (source is not a pointer)

What can I do?

melpomene
  • 84,125
  • 8
  • 85
  • 148
Ramon Pozo
  • 11
  • 2
  • 1
    `dynamic_cast` doesn't work unless `A` contains at least one virtual function. And when adding virtual functions, why not have one doing the display and skip the cast? – Bo Persson Jun 02 '18 at 18:53
  • I'll need it for other functions, in fact what I did is overcharging the << function for each, A, B and C – Ramon Pozo Jun 02 '18 at 18:58

3 Answers3

3

Apply a dynamic cast to the address of a, e.g. dynamic_cast<const B*>(&a). While it's true that a reference is a pointer under the hood (sort of), you can't just treat it as a pointer.

However, I would advise against writing something like this, which doesn't scale.

PS - As commenters suggest, you need to dynamic-cast to a const pointer, while your example casts to a non-const.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
2

As others have said, why not skip the cast? Moreover, if you want your derived classes to exhibit different behavior, why not explicitly define that different behavior? That is, using dynamic_cast<> says something about your intent—as does the alternative.

That being said, as others have also stated, your base class should have at least one virtual function and your derived classes should be publicly derived.

class A {
public:
    virtual void operator()() {
        std::cout << "A\n";
    }

    virtual void print() {
        std::cout << "A\n";
    }

    virtual ~A() = default;
};

class B : public A {
public:
    void operator()() override {
        std::cout << "B\n";
    }

    void print() override  {
        std::cout << "B\n";
    }
};

class C : public A {
public:
    void operator()() override {
        std::cout << "C\n";
    }

    void print() override {
        std::cout << "C\n";
    }
};

int main()
{
    std::vector<A*> v;
    auto pa = new A{};
    auto pb = new B{};
    auto pc = new C{};

    v.push_back(pa);
    v.push_back(pb);
    v.push_back(pc);

    for(auto& elem : v)
        elem->print();

    delete pa;
    delete pb;
    delete pc;

    return 0;
}

I think this is part of the answer to your question. I believe another portion of your question can be answered with:

std::vector<A> vv;
A a;
B b;
C c;

a(); // A
b(); // B
c(); // C

vv.push_back(a);
vv.push_back(b);
vv.push_back(c);

for(auto& elem : vv)
    elem(); // ?
eric
  • 1,029
  • 1
  • 8
  • 9
1

I fixed up your code so it would work the way you want. But I suggest you do not do this: using new/delete, new[]/delete[] and raw pointers are not best practice. Using dynamic_cast is a very bad code smell.

#include <iostream>

using std::cout;
using std::endl;

namespace
{

class A
{
public:
  virtual ~A() = default;
};

class B: public A
{
};

class C: public A
{
};

void function (A const& a)
{
  if (B const* b = dynamic_cast<B const*>(&a))
  {
    cout << "B" << endl;
  }

  if (C const* c = dynamic_cast<C const*>(&a))
  {
    cout << "C" << endl;
  }
}

} // anonymous namespace

int main ()
{
  A* array[2] =
  {
    new B,
    new C
  };

  function(*array[0]); // To print B
  function(*array[1]); // To print C
}
Eljay
  • 4,648
  • 3
  • 16
  • 27