-2

Is there a syntax in C++ to initialize an array of pointers to objects with different types without extra assignments? I tried to provide a complete example below.

#include "stdio.h"

class Base {
    public:
        Base(int cnt=1) : _cnt(cnt) {}
        virtual void print() { printf("?\n"); }
    protected:      
        int _cnt;       
};      

class A : public Base {     
    public:     
        A(int val, int cnt=1) : _val(val), Base(cnt) {}
        void print() override { for (int i=0; i<_cnt; i++) printf("A(%d)\n", _val);  }
    private:
        int _val;
};

class B : public Base {
    public:
        B(const char* val, int cnt=1) : _val(val), Base(cnt) {}
        void print() override { for (int i=0; i<_cnt; i++) printf("B(\"%s\")\n", _val);  }
    private:
        const char* _val;
};

//  *** I would like to combine the following statements ***
A a = { 42, 2 };
B b = { "hi there", 3 };

Base* test[] = { &a, &b };

int main() {
    for (auto *x : test) { x->print(); }
}

When I try

Base* test2[] = {
    &A(42, 2), 
    &B("hi there", 3),
};

I get errors for taking address of temporary. The code needs to run code on a small embedded system, so I try to avoid dynamic allocation.

Hope this is not a FAQ ...

Thanks for any help!

Matthias
  • 13
  • 3

2 Answers2

1

Your post mentions you want value semantics, which means no dynamic allocation. In the comments you mentioned that your set of types is closed. So you can go for closed set polymorhpism with boost::variant.

using common_t = boost::variant<A, B>;

common_t arr[] = {
    A(42, 2), 
    B("hi there", 3),
};

The nice thing is that the types need not have a common base anymore. They only need to respect the same interface (have print). You essentially replace dynamic with static polymorphism. Now a generic lambda can be used to access any member

for(auto &obj : arr)
  boost::apply_visitor([](auto& o) {
    o.print();
  }, obj);
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Just had a look at boost::variant and it looks great! However, Boost seems not to be available for the target system :-( I would like to +1 your answer, but my reputation is not high enough yet. – Matthias Jun 14 '17 at 10:07
  • @Matthias - Well, that's unfortunate. Boost is terrific in my opinion. And I do believe this is the best solution to your problem in-general, if platform considerations didn't apply. And don't worry about upvotes, I'm just glad I could help somehow. – StoryTeller - Unslander Monica Jun 14 '17 at 10:08
0

In your example:

Base* test2[] = {
    &A(42, 2), 
    &B("hi there", 3),
};

Both A and B are being instantiated within the scope of the array initializer. They are temporary to this scope, and this is why the compiler will not allow you to store them in the array.

You should use smart pointers to dynamically allocate your objects, since accessing a stack variable outside of it's scope is unsafe.

A good example of a smart pointer is a shared_ptr: http://en.cppreference.com/w/cpp/memory/shared_ptr

snoopy
  • 328
  • 1
  • 12
  • Thank you for the hint! However, I forgot to mention that the code needs to run on a small embedded system with very limited resources. – Matthias Jun 14 '17 at 06:55
  • @Matthias In that case change the array to store `Base` (not `Base *`), and you can instantiate them the way you have done in your example, without the `&`, of course. – snoopy Jun 14 '17 at 06:57
  • You mean like this @snoopy? `Base test2[] = { A(42, 2), B("hi there", 3), };` However, then I get the followin error: `unable to deduce 'auto*' from '* __for_begin' for (auto *x : test2) {` – Matthias Jun 14 '17 at 07:12
  • @Matthias That compile error has nothing to do with the original problem, in fact your original problem seems solved. Simply remove the * in the statement `auto *x : test`, and you should compile fine. This is because you are no longer storing pointers. – snoopy Jun 14 '17 at 07:16
  • You are right @snoopy - this is a different thing. If I change the statement to `for (auto x : test2) { x.print(); }` it compiles fine. However, the output is ? ? now. – Matthias Jun 14 '17 at 07:19
  • Won't that array declaration slice the derived classes? and cause odd things to happen. – Tom Tanner Jun 14 '17 at 07:20
  • @Matthias it is outputting "? ?" now becuase you are calling the `Base` class member function for `print`. If you want to call the `A` or `B` member print function, then you have to use `dynamic_cast` to cast the `Base` down to either `A` or `B`. http://en.cppreference.com/w/cpp/language/dynamic_cast – snoopy Jun 14 '17 at 07:25
  • @TomTanner I am unsure what "slice the derived classes" means. Could you elaborate? – snoopy Jun 14 '17 at 07:26
  • The array is of base classes, and hence the array elements are all base classes. So if a derived class is of different size to the base class, you won't get all of the derived class in the array. Just the base part of it. See (e.g.) https://stackoverflow.com/questions/274626/what-is-object-slicing – Tom Tanner Jun 14 '17 at 11:08