Resurrection
Resurrection

Reputation: 4106

How to prevent unnecessary copy of an object

I have a rapidjson wrapper that does the following:

class ADocument
{
    void setJson(const char *data) { m_D.parse(data); }

    AData operator[](const char *key) const
    {
        const rapidjson::Value *value = rapidjson::Pointer(key).Get(m_D);

        if(value)
            return AData(value);
        else
            return AData(&m_D);
    }

private:
    rapidjson::Document m_D;
};

and the AData class like this:

class AData
{
public:
    Adata(const rapidjson::Value *val) : m_Value(val) {}

    operator QString() const { return m_Value.IsString() ? m_Value.GetString() : QString(); }

private:
    const rapidjson::Value *m_Value; 
};

And the whole thing is called like this:

ADocument doc;
doc.setJson("{\"Hello\":{\"Hello\":\"test\"}}");

QString str = doc["/Hello/Hello"];

when str becomes "test".

Now by debugging this code I found out that the AData object somehow shifts - the operator QString() gets called from an object in different memory location than the original AData object is constructed in the operator[] of ADocument. Regular constructor is called once. But it might be that copy-elision simply moved the same object around in memory.

However when I define one of rule-of-three/five methods such as a destructor in AData without changing anything else (and the destructor does nothing itself) then the operator QString() is called on the SAME OBJECT (same memory location) which got constructed in the operator[] of ADocument.

Even when I implemented all thinkable constructors and operators (move, copy, assign...) NONE of them is ever called but the result is the same - only one object is ever created.

What is going on here? I would like to understand it.

And further, how to change this implementation so that it is as performance and memory efficient as possible (=minimum copies etc.)? Or maybe I am really worrying about nothing here and what I am seeing is only some compiler optimization?

Upvotes: 1

Views: 199

Answers (1)

Collin Dauphinee
Collin Dauphinee

Reputation: 13973

What is going on here? I would like to understand it.

You're experiencing copy elision.

When a unnamed temporary would be copied or moved into a variable of the same type, the compiler is allowed to construct the object directly in the variable and skip the copy or move operation.

Another situation where you may experience this is when you return a variable with automatic storage duration from a function.

Upvotes: 2

Related Questions