Why is NRVO not performed here (mutiple returns)?

89 views Asked by At

NRVO is not performed for MyString ret in the following method. The default constructed MyString (return {};) gets directly constructed into the target, ret is move constructed into the target.

MyString MyString::Substring(size_type idx, size_type length)
{
    size_type thisSize{ getSize() };
    if (thisSize - idx < length)
    {
        return {};
    }
    
    MyString ret{ length + 1 };
    for (size_type otherIdx{ 0 }; otherIdx < length; ++otherIdx)
    {
        ret[otherIdx] = (*this)[idx + otherIdx];
    }
    ret[length] = '\0';
    return ret;
}

If MyString ret{length + 1}; was above the if-statement with the return{}; in its body, it'd be clear to me why it can't be constructed directly into the target: If it were, and after that, the default constructed MyString object got returned, destruction of ret would be required, and the default constructed object would take its place. That is not desirable. But what is the underlying reason that, in this case, MyString ret can't be directly constructed into the target? When constructed in the code, it is well known that the function is not going to return {};.

I'm sure that I'm overlooking something here. Any help is greatly appreciated!

EDIT: Here's a minimal reproducible example as @rustyx suggested:

#include <iostream>

class Foo
{
public:
    Foo() = default;

    Foo(Foo&&) { std::cout << "Moved" << std::endl; }
};

Foo func(int i)
{
    if (i < 0)
    {
        return {};
    }
    Foo bar;
    return bar;
}
int main()
{
    Foo test{ func(-1) };
    Foo test1{ func(1) };
}

This prints:

(Nothing)
Moved
0

There are 0 answers