Каким правилам следует следовать, чтобы функция использовала оптимизацию NRV

#c

#c

Вопрос:

Каким правилам или советам я должен следовать, чтобы позволить функции использовать оптимизацию NRV (именованное возвращаемое значение) в функции? Я усвоил эти советы из разных мест, но не знаю, правильно ли это, насколько я понимаю:

  1. возвращаемый объект не должен иметь никакого имени внутри функции (тогда почему он называется оптимизацией возвращаемого значения !!)
  2. оператор return должен содержать объект, заключенный в круглые скобки
  3. возвращаемый объект должен иметь явный и встроенный конструктор копирования

Комментарии:

1. Я ответил вам о 1 (который, кажется, является основным моментом), но я действительно не понял 2, и я не уверен насчет 3, но я не думаю, что компилятор заботится о том, чтобы конструктор копирования был встроенным и определялся явно (хотя, вероятно, у него не должно быть ключевое слово explicit, поскольку его нужно было бы вызывать неявно, если оптимизация не происходит).

Ответ №1:

Если вы имеете в виду NRVO (оптимизацию именованного возвращаемого значения) в отличие от RVO (оптимизацию возвращаемого значения), то объект должен быть назван внутри функции. В противном случае она имела бы право только на RVO, а не на NRVO. Таким образом, есть довольно веская причина для того, чтобы называть это оптимизацией с именем возвращаемого значения.

Ознакомьтесь со следующими примерами:

RVO:

 BigObject foo(int x, int y)
{
    // Returned BigObject is created ad-hoc, and has no local 'name'.
    return BigObject(x, y);
}

void bar()
{
    BigObject obj = foo(4, 6);
}
  

Оптимизирующий компилятор будет преобразован в этот псевдокод:

 void foo(int x, int y, BigObjectamp; ret)
{
    ret._constructor_(x, y);
}

void bar()
{
    BigObject obj; // Allocate obj on the stack, but don't construct it just yet!
    foo(4, 6, obj); // Now obj is constructed by foo()
}
  

NRVO:

 BigObject foo(int x, int y, int z)
{
    // Returned BigObject has a local 'name' in foo(), which is obj.
    BigObject obj(x, y);

    // Do something with obj
    obj.setZ(z);

    return obj;
}

void bar()
{
    BigObject obj = foo(4, 6, 7);
}
  

Оптимизирующий компилятор будет преобразован в этот псевдокод:

 void foo(int x, int y, int z, BigObjectamp; ret)
{
    ret._constructor_(x, y);

    // Do something with ret
    ret.setZ(z);
}

void bar()
{
    BigObject obj; // Allocate obj on the stack, but don't construct it just yet!
    foo(4, 6, 7, obj); // Now obj is constructed by foo()
}
  

Обратите внимание, что то, произойдет ли оптимизация на самом деле, во многом зависит от компилятора. Некоторые компиляторы (например, очень старые компиляторы) вообще не будут выполнять RVO или NRVO, в то время как другие компиляторы могут иметь другие ограничения на эту оптимизацию. Вот описание ограничений, которые MSVC накладывает на NRVO:
http://msdn.microsoft.com/en-us/library/ms364057 (v = против80).aspx#nrvo_cpp05_topic3

Комментарии:

1. аааа! я ошибочно думал, что RVO — это сокращенная форма NRVO

2. Нет. Оба выполняются компилятором, и пользователь вообще не может ими управлять. NRVO выполняется, когда объект имеет «локальное имя» в возвращающей функции, а RVO выполняется, когда он создается в той же строке, что и return оператор.