Search code examples
c++memory-managementstandards

When is a temporary object created in a function argument supposed to be destroyed?


Short version

In this code :

QString dataStr;

myfunc(dataStr.toUtf8().constData());

Is the temporary QByteArray object created by toUtf8() destroyed before entering the scope of myfunc() or is it guaranteed to be destroyed after the call to myfunc() is finished?

Long version

I am working with a code similar to this one :

class Request
{
public :
    std::function<void()> requestFunc;
};

void myfunc(const char* data)
{
    auto request = new Request();

    auto lambda = [](const std::string& str)
    {
        cout << str;
    }
    // Use data
    request->requestFunc = std::bind(std::move(lambda), std::string(data));
    requestQueue.push(std::move(request));
}

QString dataStr;

myfunc(dataStr.toUtf8().constData());

// Some time later, requestQueue gets managed and requestFunc gets called

One the machine of one of my coworkers, it crashed. We resolved this explicitly changing the scope of the temporary QByteArray object created by toUtf8() :

...
QString dataStr;

const QByteArray tempObj = dataStr.toUtf8();
myfunc(tempObj.constData());

And it resolved the crash. But later we a discussion with another coworker, and it isn't obvious to me if the C++ standard / the compiler is supposed to keep the temporary object until the function call is finished, or if its scope is limited to the parameter, and thus once inside myfunc() the temporary parameter object is already destroyed.

The two possibilities that I see that explain the "fix" of the crash :

  • The temp object is indeed destroyed before entering the function, and constData() is pointing to freed memory. As not much happens before it is used, the memory is usually still holding the right value and has not been overwritten. That's why it was working most of the cases even being wrong, and the new version is the correct fix.
  • The temporary object is only destroyed after the function call is finished. It means that the bug is elsewhere, and the "fix" only worked because we delayed the destruction of the temporary object, and thus lowered the likelihood of freed memory being overwritten.

If this is the latter case, than do you know where the bug might be laying?

EDIT :

It turns out the bug was with my previous code. Before the change I was doing this :

QString dataStr;
const char* dataPtr = dataStr.toUtf8().constData();
myfunc(dataPtr);

So the temporary object was being destroyed before the call to myfunc()


Solution

  • Is the temporary QByteArray object created by toUtf8() destroyed before entering the scope of myfunc() or is it guaranteed to be destroyed after the call to myfunc() is finished?

    The object is not a parameter object. It is a temporary object. A temporary object is destroyed at the end of the full-expression in which it was created, although some exceptions apply, none of which is relevant to this example. Note that a parameter object, i.e. the object corresponding to a non-reference parameter of a function, is not a temporary object and follows other rules.

    The end of the full-expression in your example is after the evaluation of myfunc(dataStr.toUtf8().constData()), i.e. at the end of the expression statement.

    In your modified example in the question edit, the end of the full-expression in which dataStr.toUtf8()'s result object is materialized is after the initialization of the variable dataPtr, which means that in the following call myfunc(dataPtr), the object is already destroyed and dataPtr is dangling.