Сначала рассмотрим подвыражение play(5)
. Это выражение одинаково в обоих случаях.
В выражении вызова функции каждый параметр инициализируется копией своего аргумента (ISO / IEC 14882: 2003 5.2.2 / 4). В этом случае это включает преобразование 5
в B
с использованием неявного конструктора, принимающего int
для создания временного B
, а затем с помощью конструктора копирования для инициализации параметра b
. Однако в реализации разрешено исключать временные объекты путем прямой инициализации b
с использованием конструктора преобразования из int
в соответствии с правилами, указанными в 12.8.
Тип play(5)
- это B
и - как функция, возвращающая не-ссылку - это rvalue .
Оператор return
неявно преобразует возвращаемое выражение в тип возвращаемого значения (6.6.3), а затем копирует-инициализирует (8.5 / 12) возвращаемый объект с преобразованным выражением.
В этом случае возвращаемое выражение уже имеет правильный тип, поэтому преобразование не требуется, но инициализация копии по-прежнему требуется.
Помимо оптимизации возвращаемого значения
Именованная оптимизация возвращаемого значения (NRVO) относится к ситуации, в которой оператор возврата имеет вид, если форма return x;
, где x
- автоматический объект, локальный для функции. Когда это происходит, реализации разрешается построить x
в местоположении для возвращаемого значения и исключить инициализацию копирования в точке return
.
Хотя в стандарте оно не названо таковым, NRVO обычно относится к первой ситуации, описанной в 12.8 / 15.
Эта конкретная оптимизация невозможна в play
, поскольку b
не является объектом, локальным для тела функции, это имя параметра, который уже был создан к моменту ввода функции.
(Безымянная) оптимизация возвращаемого значения (RVO) еще меньше согласуется с тем, на что она ссылается, но обычно используется для ссылки на ситуацию, когда возвращаемое выражение не является именованным объектом, а выражением, где преобразование в возвращаемый тип и copy-initialization возвращаемого объекта можно объединить так, чтобы возвращаемый объект инициализировался прямо из результата преобразования, исключающего один временный объект.
RVO не применяется в play
, потому что b
уже имеет тип B
, поэтому copy-initialization эквивалентен direct-initialization и не является временным объект необходим.
В обоих случаях play(5)
требует построения B
с использованием B(int)
для параметра и инициализации копирования B
для возвращаемого объекта. Он также может использовать вторую копию при инициализации параметра, но многие компиляторы исключают эту копию, даже если оптимизация явно не запрашивается. Оба (или все) из этих объектов являются временными.
В выражении выражения t1 = play(5);
будет вызван оператор присваивания для копирования значения возвращаемого значения play
в t1
, и два временных значения (параметр и возвращаемое значение play
) будут уничтожены , Естественно, t1
должно быть построено до этого утверждения, и его деструктор будет вызван в конце срока его жизни.
В операторе объявления B t1 = play(5);
логически t1
инициализируется с возвращаемым значением play, и в качестве выражения выражения t1 = play(5);
будет использовано точно такое же количество временных. Однако это вторая из ситуаций, описанных в 12.8 / 15, когда реализация позволяет исключить временное значение, используемое для возвращаемого значения play
, и вместо этого разрешить возвращаемому объекту псевдоним t1
. Функция play
работает точно так же, но поскольку возвращаемый объект является просто псевдонимом t1
, ее оператор return эффективно инициализирует t1
, и не существует отдельного временного объекта для возвращаемого значения, которое необходимо уничтожить. .