Почему конструктор перемещения вызывается только один раз? - PullRequest
0 голосов
/ 12 декабря 2018

У меня есть следующая функция:

SomeClass func()
{
    SomeClass someObject;
    someObject.mutate("some text");
    return someObject;
}

и следующие два случая:

1

int main()
{
    func();
    return 0;
}

2

int main()
{
    SomeClass someObject = func();
    return 0;
}

У меня отключено NRVO, исключение копирования / перемещения не происходит.

В обоих случаях у меня один и тот же вывод:

"default constructor"
"move constructor"

Почему конструктор перемещения вызывается только один раз в случае 2?Я ожидал, что он будет вызван один раз для возвращаемого значения функции, а второй - для инициализации переменной someObject.

ОБНОВЛЕНИЕ: Для большей ясности: вывод предназначен для отладочной сборки.Для сборки релиза у меня есть только «конструктор по умолчанию», и это кажется мне понятным из-за возможности копирования / перемещения.Я хочу понять другой вывод для отладочной сборки.

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Итак, должны возникнуть два случая elision: один для оператора return, а другой для инициализации.Без каких-либо ограничений выходные данные должны быть следующими: конструктор по умолчанию, конструктор перемещения, конструктор перемещения.Некоторые компиляторы предлагают варианты отключения elision.Протестировано с GCC с -fno-elide-constructors, и это поведение было подтверждено.

Теперь при включенном elision поведение особенно зависит от используемого стандарта C ++ (C ++ 11 / C ++ 14 / C ++17) а во вторых похоже тоже на компилятор.Есть случаи исключения, которые происходят после C ++ 11, другие после C ++ 14 и другие после C ++ 17.

В случае 2, GCC C ++ 14 (и C ++ 11)применяется оба варианта, где компилятор MSVC C ++ 14 применяет только один (еще не определено, какой).

0 голосов
/ 12 декабря 2018

Это связано с возможностью копирования в C ++ 17.From cppreference :

При следующих обстоятельствах компиляторам разрешено, но не требуется пропускать конструкцию объектов класса копирования и перемещения (начиная с C ++ 11), даже есликонструктор копирования / перемещения (начиная с C ++ 11) и деструктор имеют наблюдаемые побочные эффекты.
[...]
При инициализации объекта, когда исходный объект является безымянным временным и имееттот же тип класса (игнорирующий квалификацию cv), что и целевой объект.Когда безымянный временный оператор является операндом оператора возврата, этот вариант исключения копии известен как RVO, «оптимизация возвращаемого значения».
Эта оптимизация является обязательной;смотри выше.(начиная с C ++ 17)

См. также копирование инициализации.Также из cppreference :

Эффект инициализации копирования:
Во-первых, если T является типом класса, а инициализатор является выражением prvalue, тип которого cv-unqualifiedтот же класс, что и у T, само выражение инициализатора, а не временное материализованное из него, используется для инициализации целевого объекта: см. elision копии (начиная с C ++ 17)

Обратите внимание, что этоне NRVO ( Named Оптимизация возвращаемого значения).Это RVO.

В C ++ 14 оптимизация не происходит, если вы этого не хотите (см. -fno-elide-constructors).
Демо (с GCC, но Clang выдает тот же результат)

В C ++ 17 это обязательно, поэтому оно имеет место.
Demo (опять же, GCC, но Clang соглашается)

...