Я думаю, это должно, потому что это важно для правильности. Тем не менее, я удивлен, увидев выход Clang. Рассмотрим следующий код:
#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";
}
Мы определили перемещение ctor для S
, чтобы проверить, вызывается ли S(S&&)
, если нет, применяется NRVO.
Результат из GCC:
0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42
применяется NRVO, и они принимают тот же адрес, который ожидается.
Однако, Clang output:
0x7fff908bbcc8
0x7fff908bbcf8
42
применяется NRVO, но адреса различаются.
В случае, если вы задаетесь вопросом, почему важно иметь тот же адрес, потому что какой-то объект может сделать некоторую регистрацию с его адресом при построении, и если объект перемещен, он должен быть уведомлен (например, через move-ctor).
Наличие NRVO, но с другим адресом памяти, тем самым делает его плохо сформированным. Это явное нарушение контракта - не вызывается пользовательский move/copy ctor, как компилятор "копирует" данные S в другое место?
Является ли это ошибкой в Clang?
Если мы добавим деструктор к S
, например
~S() {}
В этот раз, Clang выводит один и тот же адрес.