Копирование разрешалось при ряде обстоятельств. Однако, даже если это было разрешено, код все равно должен был работать так, как если бы копия не была опущена. А именно, должен быть доступный конструктор копирования и / или перемещения.
Гарантированное исключение копирования переопределяет ряд концепций C ++, так что определенные обстоятельства, при которых копии / перемещения могут быть исключены, фактически не вызывают копирование / перемещение вообще. Компилятор не исключает копию; в стандарте сказано, что такого копирования никогда не может быть.
Рассмотрим эту функцию:
T Func() {return T();}
Согласно правилам негарантированной копии, это создаст временное, а затем перейдет из этого временного в возвращаемое значение функции. Эту операцию перемещения можно пропустить, но T
по-прежнему должен иметь доступный конструктор перемещения, даже если он никогда не используется.
Сходным образом:
T t = Func();
Это инициализация копии t
. Это скопирует инициализацию t
с возвращаемым значением Func
. Однако T
по-прежнему должен иметь конструктор перемещения, даже если он не будет вызываться.
Гарантированное исключение копирования переопределяет значение выражения prvalue. До C ++ 17 prvalue были временными объектами. В C ++ 17 выражение prvalue - это просто то, что может материализовать временное, но это еще не временное явление.
Если вы используете prvalue для инициализации объекта типа prvalue, временное значение не материализуется. Когда вы выполняете return T();
, это инициализирует возвращаемое значение функции через prvalue. Поскольку эта функция возвращает T
, временные данные не создаются; инициализация prvalue просто напрямую инициирует возвращаемое значение.
Следует понимать, что, поскольку возвращаемое значение является значением prvalue, это еще не объект. Это просто инициализатор объекта, как и T()
.
Когда вы делаете T t = Func();
, prvalue возвращаемого значения напрямую инициализирует объект t
; отсутствует этап «создание временного и копирование / перемещение». Поскольку возвращаемое значение Func()
является prvalue, эквивалентным T()
, t
напрямую инициализируется T()
, точно так же, как если бы вы сделали T t = T()
.
Если prvalue используется каким-либо другим образом, prvalue материализует временный объект, который будет использоваться в этом выражении (или отброшен, если выражение отсутствует). Итак, если вы сделали const T &rt = Func();
, значение prvalue материализовалось бы как временное (используя T()
в качестве инициализатора), ссылка на который будет храниться в rt
вместе с обычным временным материалом для продления срока службы.
Одна вещь, которую вам разрешает гарантированная элизия, - это возвращать неподвижные объекты. Например, lock_guard
нельзя скопировать или переместить, поэтому у вас не может быть функции, возвращающей его по значению. Но с гарантированным копированием вы можете.
Гарантированное исключение также работает с прямой инициализацией:
new T(FactoryFunction());
Если FactoryFunction
возвращает T
по значению, это выражение не копирует возвращаемое значение в выделенную память. Вместо этого он будет выделять память и использовать выделенную память в качестве памяти возвращаемых значений для прямого вызова функции.
Таким образом, фабричные функции, которые возвращаются по значению, могут напрямую инициализировать память, выделенную кучей, даже не подозревая об этом. Конечно, пока эти функции внутри следуют правилам гарантированного исключения копий. Они должны вернуть значение типа T
.
Конечно, это тоже работает:
new auto(FactoryFunction());
Если вам не нравится писать имена типов.
Важно понимать, что вышеуказанные гарантии работают только для prvalues. То есть вы не получаете никакой гарантии при возврате именованной переменной:
T Func()
{
T t = ...;
...
return t;
}
В этом случае t
все еще должен иметь доступный конструктор копирования / перемещения. Да, компилятор может выбрать оптимизацию копирования / перемещения. Но компилятор по-прежнему должен проверять наличие доступного конструктора копирования / перемещения.
Таким образом, для оптимизации именованного возвращаемого значения (NRVO) ничего не меняется.
person
Nicol Bolas
schedule
26.06.2016