Является ли поведение гарантированного экземпляра копии зависимым от существования пользовательского конструктора копирования?

Следующий код ведет себя иначе с или без пользовательского конструктора копирования в GCC 8.0.1:

#include <cassert>

struct S {
    int i;
    int *p;
    S() : i(0), p(&i) {}
//    S(const S &s) : i(s.i), p(&i) {}  // #1
//    S(const S &s) : i(s.i), p(s.p) {} // #2
//    S(const S &s) = delete;           // #3
};

S make_S() {return S{};}

int main()
{
    S s = make_S();
    assert(s.p == &s.i);
}

С любым из комментариев созданных пользователем конструкторов копий (даже С# 2, который выполняет простую мелкую копию), это утверждение не сработает, что означает гарантированное копирование elision работает как ожидалось.

Однако, без какого-либо пользовательского конструктора копирования, утверждение не выполняется, что означает, что объект s в main не построен по умолчанию. Почему это происходит? Здесь не гарантируется копирование?

Ответ 1

Цитата из С++ 17 Рабочий проект §15.2 Временные объекты Пункт 3 (https://timsong-cpp.github.io/cppwp/class.temporary#3):

Когда объект класса X передается или возвращается из функции, если каждый конструктор копирования, перемещение конструктора и деструктор X либо тривиальны, либо удалены, а X имеет по крайней мере один не удаленный экземпляр или механизм перемещения, реализациям разрешено создавать временный объект для хранения параметра функции или объекта результата.... [Примечание. Эта широта предоставляется, чтобы объекты типа класса были переданы или возвращены из функций в регистрах. - конечная нота]

В вашем случае, когда я сделал как конструкторы копирования, так и перемещения по умолчанию:

S(const S &) = default;
S(S &&) = default;
Утверждение

также не удавалось с GCC и Clang. Обратите внимание, что неявно определенные конструкторы тривиальны.