1

Using raw pointers, this can be achieved like so:

#include <iostream>
#include <string>
#include <map>

using namespace std;

class base {
public:
    virtual ~base(){}
};

class derived1 : public base {
public:
    virtual ~derived1(){}
    derived1(const int& other) : val(other) {}
    int val;
};

class derived2 : public base {
public:
    virtual ~derived2(){}
    derived2(const float& other) : val(other) {}
    float val;
};


int main()
{
    derived1 dataA = 4;
    derived2 dataB = 3.0f;

    std::map<std::string, base*> mapOfPointersToData; //Needs to be a pointer because it can be of type deribed1 or derived2. Cant be smart as pointing to stack memory
    mapOfPointersToData["dataA"] = &dataA;
    mapOfPointersToData["dataB"] = &dataB;

    base* result = mapOfPointersToData["dataA"];

    std::cout << dynamic_cast<derived1*>(result)->val << std::endl;

    return 0;
}

This works, but I have been told that we should avoid the use of raw pointers in favour of smart pointers or references.

The objects can never be null, so it makes sense to use a reference. We can get around the problem that the map can only store one data type by using a variant:

int main()
{
    derived1 dataA = 4;
    derived2 dataB = 3.0f;

    std::map<std::string, std::variant<derived1, derived2>&> mapOfPointersToData; //Cant be constructed
    mapOfPointersToData["dataA"] = dataA;
    mapOfPointersToData["dataB"] = dataB;

    auto result = mapOfPointersToData["dataA"];

    std::cout << std::get<derived1>(result).val << std::endl;

    return 0;
}

but this gives the error

error: value-initialization of reference type ‘std::variant<derived1, derived2>&

So what is the best way to store references to stack data that can be of different types?

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Blue7
  • 1,750
  • 4
  • 31
  • 55

2 Answers2

5

This works, but I have been told that we should avoid the use of raw pointers in favour of smart pointers or references.

Raw pointers are only discouraged when they are responsible for owning what they point to (essentially, if you have to remember to delete or otherwise release them). Here, the objects have automatic storage duration and the pointers aren't responsible for cleaning them up. This is a reasonable use case for raw pointers.

The objects can never be null, so it makes sense to use a reference.

Objects in your map might be nullptr if you use operator[] to search the map. That operator adds any element it fails to find and value initializes them (to nullptr for pointers). You can use find instead if you don't want to bloat your map with empty pointers.

So what is the best way to store references to stack data, that can be of different types?

You might be looking for std::reference_wrapper which wraps references in an object that is suitable for containers. But using base* seems like a fine solution already, because your map doesn't own the objects it ultimately points to.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
1

If your objective is to avoid raw pointers in favour of smart pointer, this is what I would do:

int main()
{
    std::map<std::string, std::unique_ptr<base>> mapOfPointersToData;
    mapOfPointersToData["dataA"] = std::make_unique<derived1>(4);
    mapOfPointersToData["dataB"] = std::make_unique<derived2>(3.0f);

    auto& result = mapOfPointersToData["dataA"];

    std::cout << dynamic_cast<derived1*>(result.get())->val << std::endl;

    return 0;
}

Note that I put the object creation into the heap using make_unique<>() calls to highlight the ownership by smart pointer.

adrtam
  • 6,991
  • 2
  • 12
  • 27
  • Avoiding raw pointers for the sake of avoiding them is as harmful as using them for resources owners. In the above case he does not mention the fact that the map should be the sole owner and therefore a raw pointer is likely more suitable. – const_ref Feb 13 '20 at 22:10
  • The question specifies that the pointed objects "[have] been allocated on the stack". – François Andrieux Feb 13 '20 at 22:13
  • As François mentioned, the object being pointed to exists on the stack – Blue7 Feb 13 '20 at 22:24
  • @Blue7 in that case, you should not use smart pointers. Smart pointer means ownership by the pointer and therefore it cannot be created in the stack. – adrtam Feb 13 '20 at 22:26