Компиляторы C ++ избегают копирования при возврате по значению? - PullRequest
1 голос
/ 22 ноября 2011

Рассмотрим следующий код:

LargeObject getLargeObject()
{
    LargeObject glo;
    // do some initialization stuff with glo
    return glo;
}

void test()
{
    LargeObject tlo = getLargeObject();
    // do sth. with tlo;
}

Простой компилятор создаст локальный объект LargeObject glo в стеке getLargeObject (), а затем при возврате назначит его tlo в test (), что включает операцию копирования..

Но не должен ли умный компилятор понять, что glo будет назначен на tlo, и, таким образом, просто использовать память tlo в первую очередь, чтобы избежать операции копирования?В результате чего-то (функционально) вроде:

void getLargeObject(LargeObject &lo)
{
    // do init stuff
}

void test()
{
    LargeObject lo;
    getLargeObject(lo);
}

Я предполагаю, что компиляторы делают что-то подобное.Но всегда ли это можно сделать?Есть ли ситуации, когда его нельзя так оптимизировать?Как я могу узнать, скопировано ли мое возвращаемое значение или нет?

Ответы [ 3 ]

4 голосов
/ 22 ноября 2011

Ваше предположение верно.И да, существуют ситуации, когда это невозможно сделать, например:

LargeObject getLargeObject()
{
    LargeObject glo1, glo2;
    // do some initialization stuff         
    if (rand() % 2)
        return glo1;
    return glo2;
}

Это невозможно сделать, потому что компилятор не может знать, будет ли он использовать glo1 или glo2 для возвращаемого значения.

«Как я могу узнать, скопировано ли мое возвращаемое значение или нет?»

Два способа, о которых я могу думать.Вы можете создавать шумные конструкторы копирования.То есть копируют конструкторы, которые имеют некоторый обнаружимый побочный эффект, такой как печать сообщения.Тогда конечно есть старый взгляд на сборку.

2 голосов
/ 22 ноября 2011

Для начала, даже наивный компилятор не назначит & ldquo; tlo & rdquo ;, поскольку стандарт не позволяет этого. Формальная семантика вашего кода включает две копии (обе используют конструктор копирования); первый от glo до временного возвращаемого значения, а второй от этого временное возвращаемое значение до tlo. Стандарт, однако, формально дает составители право исключить обе эти копии, в этом конкретном случай, и практически говоря, я представляю, что все компиляторы делают.

Первая копия может быть подавлена ​​в любое время, когда вы возвращаете локальную переменную или временный; некоторые компиляторы этого не делают, если их больше одного return в коде, однако (но это никогда не будет иметь место в хорошо письменный код).

Подавление второй копии зависит от того, что вы Построение нового объекта на сайте вызова. Если вы не строите новый объект, тогда может даже не быть второй копии для подавления; например в случае как getLargeObject().memberFunction(). Если вы назначаете к существующему объекту, однако, компилятор мало что может сделать; Это должен вызвать оператор присваивания. Если оператор присваивания копирует, тогда вы получите эту копию.

2 голосов
/ 22 ноября 2011

Да, это должно. Это называется оптимизацией именованного возвращаемого значения (NRVO или просто RVO).

...