Search code examples
c++vectorpolymorphismshared-ptr

How to "reassign" a shared_ptr<base> to a new derived object


I'm writing an interpretive language using C++. Now the problem is I want to implement features like reassignment:

VAR a = [1,"2",[3,4]]
VAR a[0] = 100

In my language, a List is a vector of shared_ptr<Data>, so that you can store different types of data in one list. If someone wants to change an element, I can get the shared_ptr<Data> elem, and the shared_ptr<Data> value that's about to be assigned.

Something like:

shared_ptr<Data> elem = visit(elem_node)
shared_ptr<Data> value = visit(value_node)
*elem = *value;

Sorry, I forgot to say that visit() returns a value, not a reference, that's why I didn't just let elem=value;

It turns out the only thing that has been changed is the data member from the Data class.

But what I want is to let the shared_ptr "repoint" to a new object. Is it possible?

I've tried dynamic_pointer_cast, if the origin element and the new value are of the same type, everything is fine. But as I pointed out, I allow different types of elements in one list, so this can only be my last straw.

I've written a demo code based on Remy Lebeau's answer(thanks):

#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

struct Data
{
    virtual ~Data() = default;
    virtual void print() = 0;
};

struct Integer : Data
{
    int value;
    Integer(int val) : value(val) {}
    void print() override { cout << "integer(" << value << ")" << endl; }
};

struct String : Data
{
    string value;
    String(string val) : value(val) {}
    void print() override { cout << "string(\"" << value << "\")" << endl; }
};

struct RuntimeResult
{
    RuntimeResult success(const shared_ptr<Data> &value)
    {
        this->value = value;
        return (*this);
    }

    shared_ptr<Data> registry(const RuntimeResult &res)
    {
        // this is for handling error
        // but this simplify version simply return value;
        return res.value;
    }

    shared_ptr<Data> value;
};

int main()
{
    // simulate a List variable
    vector<shared_ptr<Data>> list;
    list.push_back(make_shared<Integer>(123456));
    list.push_back(make_shared<String>("hello"));

    RuntimeResult res;

    // inside registry, it should be something like visit_IndexNode(elem_node)
    // to interprete a node on AST Tree
    // but the return is simply like what have shown below
    shared_ptr<Data> elem = res.registry(RuntimeResult().success(list.at(0)));
    // Using Visual Studio 2019's debugging mode
    // we can see that elem's ptr == list[0]'s ptr
    // in other word, they are pointing to the same thing(123456)

    shared_ptr<Data> value = res.registry(RuntimeResult().success(make_shared<String>("test")));

    elem = value;
    elem->print(); // "test"

    list[0]->print(); // still 123456

    return 0;
}

this shows exactly my problem


Solution

  • But what I want is to let the shared_ptr "repoint" to a new object. Is it possible?

    Yes. Simply assign one shared_ptr<Data> to another, no casting needed:

    shared_ptr<Data> &elem = ...;
    shared_ptr<Data> value = ...;
    elem = value;
    

    Online Demo