Произойдет ли RVO при возврате std::pair?

Функция должна вернуть вызывающей стороне два значения. Каков наилучший способ реализации?

Опция 1:

pair<U,V> myfunc()
{
...
return make_pair(getU(),getV());
}

pair<U,V> mypair = myfunc();

Вариант 1.1:

// Same defn
U u; V v;
tie(u,v) = myfunc();

Вариант 2:

void myfunc(U& u , V& v)
{
u = getU(); v= getV();
}

U u; V v;
myfunc(u,v);

Я знаю с Option2, нет никаких копий / ходов, но это выглядит некрасиво. Будут ли происходить какие-либо копии / перемещения в Варианте 1, 1.1? Предположим, что U и V - огромные объекты, поддерживающие обе операции копирования / перемещения.

Вопрос: теоретически возможно ли для какой-либо оптимизации RVO/NRVO согласно стандарту? Если да, реализован ли gcc или любой другой компилятор?

3 ответа

Произойдет ли RVO при возвращении std::pair ?

Да, оно может.

Это гарантированно произойдет?

Нет.


Стандарт C++11: раздел 12.8/31:

При соблюдении определенных критериев реализация может опустить конструкцию копирования / перемещения объекта класса, даже если конструктор копирования / перемещения и / или деструктор для объекта имеют побочные эффекты.

Копирование elision не является гарантированной функцией. Это оптимизация, которую компиляторы могут выполнять, когда могут. В этом нет ничего особенного std::pair, Если компилятор достаточно хорош, чтобы обнаружить возможность оптимизации, он это сделает. Так что ваш вопрос зависит от компилятора, но да, то же правило относится к std::pair как и для любого другого класса.

Хотя RVO не гарантируется, в C++11 функция, как вы ее определили, я ДОЛЖНА, по крайней мере, ДОЛЖНА переместить-вернуть, поэтому я бы предложил оставить более четкое определение, а не деформировать его, чтобы принимать выходные переменные (если у вас нет конкретная политика их использования).

Кроме того, даже если в этом примере использовался RVO, ваше явное использование make_pair означает, что у вас всегда будет хотя бы одна дополнительная конструкция пары и, следовательно, операция перемещения. Измените его, чтобы оно возвращало инициализированное скобками выражение:

return { getU(), getV() };

Разрешение RVO или Copy зависит от компилятора, поэтому, если вы хотите использовать RVO и избегать вызова конструктора Copy, лучше всего использовать указатели.

В нашем продукте мы используем указатели и указатели контейнеров повышения, чтобы избежать конструктора копирования. и это действительно дает прирост производительности примерно на 10%.

Возвращаясь к вашему вопросу, в варианте 1 U и конструктор копирования V не будут вызываться, так как вы не возвращаете U или V, а возвращаете объект std::pair, поэтому вызывается его конструктор копирования, и большинство компиляторов здесь обязательно используют RVO, чтобы избежать этого.,

Спасибо нирадж рати

Если вам нужно проделать дополнительную работу над u а также v после создания пары я нахожу следующий шаблон довольно гибким в C++17:

pair<U,V> myfunc()
{
  auto out = make_pair(getU(),getV());
  auto& [u, v] = out;
  // Work with u and v
  return out;
}

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

Другие вопросы по тегам