Зависит ли поведение гарантированного исключения копирования от существования определяемого пользователем конструктора копирования?

Следующий код ведет себя по-разному с пользовательским конструктором копирования или без него в 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, выполняющим простую неглубокую копию) утверждение не завершится ошибкой, что означает гарантированная копия работает должным образом.

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


person xskxzr    schedule 20.02.2018    source источник
comment
Гарантированное копирование и RVO - это не одно и то же.   -  person StoryTeller - Unslander Monica    schedule 20.02.2018
comment
Ваш компилятор полностью поддерживает эту функцию?   -  person juanchopanza    schedule 20.02.2018
comment
Также стоит упомянуть, что он не работает и на Clang 7.0.0.   -  person StoryTeller - Unslander Monica    schedule 20.02.2018
comment
@StoryTeller RVO в этом примере соответствует критериям гарантированного копирования. Это инициализация из prvalue того же типа класса.   -  person xskxzr    schedule 20.02.2018
comment
@juanchopanza Кажется, GCC полностью поддерживает гарантированное исключение копирования, начиная с версии 7 из этой таблицы   -  person xskxzr    schedule 20.02.2018
comment
Я, наверное, ошибаюсь, но я действительно не могу найти в вашем примере ничего плохого, что сделало бы копирование ошибочным. Я подозреваю, что проблема с QOI (как для Clang, так и для GCC).   -  person StoryTeller - Unslander Monica    schedule 20.02.2018
comment
Просто для пояснения, в C ++ 17 нет RVO, поскольку временное значение не материализуется, когда возвращается prvalue. Тогда нет никаких конструкторов, которые следует исключать.   -  person Daniel Langr    schedule 20.02.2018


Ответы (1)


Цитата из рабочего проекта C ++ 17 §15.2 «Временные объекты», параграф 3 (https://timong-cpp.github.io/cppwp/class.Contemporary#3):

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

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

S(const S &) = default;
S(S &&) = default;

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

person Daniel Langr    schedule 20.02.2018
comment
И что вы знаете, увеличение размера объекта для кода OP внезапно заставляет все это работать. Я думаю, потому что это больше не помещается в реестр. - person StoryTeller - Unslander Monica; 20.02.2018
comment
@StoryTeller Точно, мне пришло в голову провести такой же эксперимент. Но ты был быстрее :) - person Daniel Langr; 20.02.2018