I think it should, because it's important for correctness. However, I'm surprised to see Clang's output. Consider the code below:
#include <iostream>
struct S
{
int i;
S(int i) : i(i) {}
S(S&&)
{
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
S f()
{
S s{42};
std::cout << &s << "\n";
return s;
}
int main()
{
S s{f()};
std::cout << &s << "\n";
std::cout << s.i << "\n";
}
We defined a move ctor for S
to check if S(S&&)
is called, if not, NRVO is applied.
The result we see from GCC is:
0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42
NRVO is applied and they take the same address, which is expected.
However, Clang's output:
0x7fff908bbcc8
0x7fff908bbcf8
42
NRVO is applied but the addresses differ.
In case you wonder why having the same address is important - it's because some object may do some registration with its address at construction, and if the object is moved, it should be notified (e.g. via move-ctor).
Having NRVO applied but with different memory address thus makes it ill-formed. It's a clear violation of the contract - no custom move/copy ctor is called, how could the compiler "copy" the data of S to a different place?
Is this a bug in Clang?
If we add a destructor to S
, e.g.
~S() {}
This time, Clang outputs the same address.
Definitely seems to be a bug in clang, they should be the same, else things like the following will be erroneous
struct S
{
int i;
int* ptr;
S(int i) : i(i) {
this->ptr = &this->i;
}
S(S&& s)
{
this->i = s.i;
this->ptr = &this->i;
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
Where a move (or elision where addresses don't change) is required to ensure that the internal pointer points to the correct integer. But because of elision that pointer points to memory that does not contain the member integer.
See output here https://wandbox.org/permlink/NgNR0mupCfnnmlhK
As pointed out by @T.C., this is actually a bug in the Itanium ABI spec that doesn't take move-ctor into account. Quoting from Clang's dev:
Clang's rule is the one in the ABI: a class is passed indirectly if it has a non-trivial destructor or a non-trivial copy constructor. This rule definitely needs some adjustment [...]
Indeed, if we define either a non-trivial dtor or copy-ctor for S
in the original example, we get the expected result (i.e. same address).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With