Sep 23, 2018

[C++] std::move(R-VALUE) undefined behavior? No.

Callee's RVO r-value is allocated on caller's stack, thus
std::move a returned r-value and reference to it isn't UB.
https://godbolt.org/z/-CllrO
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    return T{};
}


T layer2()
{
    return layer1();
}

T layer3()
{
    return layer2();
}


int main()
{
    T const &ref{std::move(layer3())};
    T &&ref2{std::move(layer3())};
    int const &ref3{layer3().A[99]};
    int const &ref4{layer3().A[999]};
}

A NRVO will be allocated on caller's stack as well.
https://godbolt.org/z/iqbhCI
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    T t{};
    return t;
}


int main()
{

    int const &ref4{layer1().A[999]};
}

However, if we try to out smart the compiler using std::move for NRVO, compiler has no idea what's inside the std::move thus
create the large object on callee's stack and then do a
memcpy back to caller's stack.
https://godbolt.org/z/ql7B9i
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    T t{};
    return std::move(t);
}


int main()
{

    int const &ref4{layer1().A[999]};
}


Some UB examples here https://github.com/jeaye/value-category-cheatsheet
is not correct.

Further reading:
https://abseil.io/tips/11

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.