Играя с RVO, я столкнулся со следующей проблемой, и я не могу понять ее.
#include <iostream>
struct A {
A* p = this;
};
A func()
{
return A();
}
int main()
{
A a = A();
std::cout << "address of a: " << &a << " - a.p = " << a.p << std::endl;
A b = func();
std::cout << "address of b: " << &b << " - b.p = " << b.p << std::endl;
}
Результат:
address of a: 0x7ffe5b11b7a0 - a.p = 0x7ffe5b11b7a0
address of b: 0x7ffe5b11b790 - b.p = 0x7ffe5b11b770
С a
все в порядке. Однако обратите внимание, что b.p
на самом деле не указывает на b
, как если бы он указывал на другой объект.
Я использую gcc 8.3.0, который поддерживает C++17. Насколько я понимаю, начиная с С++ 17, RVO является обязательным, и инициализация из значения prvalue (т.е. A b = func();
) также должна вызывать обязательное исключение. Следовательно, теоретически для инициализации b
должен вызываться только один конструктор, поэтому p
не должен указывать ни на что другое, кроме b
. Может ли кто-нибудь объяснить мне, что здесь происходит?
Примечание: инициализация p
в конструкторе по умолчанию вместо использования инициализации члена по умолчанию не меняет поведение.
Я еще немного поиграл с этим и понял, что определение конструктора по умолчанию И любой из следующих специальных функций заставляет p
указывать на b
, как и ожидалось:
- деструктор
- конструктор перемещения
- оператор присваивания перемещения
- конструктор копирования
Вместо того, чтобы помочь мне понять проблему, это еще больше запутало меня...
Кстати, я наблюдал точно такое же поведение с gcc 11.0.0 и clang 11.0.0 (используя Wandbox), так что это вряд ли ошибка.