Принудительное построение RVO/перемещения при возврате по значению

Скажем, у меня есть объект 'foo' с конструктором копирования и конструктором перемещения, а также функция

foo f() {
    foo bar;
    /* do some work */
    return bar;
}

В стандарте указано, что компилятор попытается сделать: NRVO, возврат по r-значению ref, возврат по значению, сбой; в этой последовательности.

Есть ли способ заставить компилятор никогда не возвращать значение, поскольку мой конструктор копирования довольно дорог?


person C. Broadbent    schedule 26.11.2012    source источник
comment
Вы профилировали и определили, что существует фактическое узкое место?   -  person Kerrek SB    schedule 26.11.2012
comment
Стандарт определяет NRVO? Новость для меня...   -  person Lightness Races in Orbit    schedule 26.11.2012
comment
(Я признаю, что это позволяет NRVO)   -  person Lightness Races in Orbit    schedule 26.11.2012
comment
Он «позволяет» NRVO и «позволяет» возвращать по ссылке rvalue, но, похоже, не требует ни того, ни другого, кроме приоритета, если они происходят. По сути, я хочу, чтобы это произошло, но никогда не было конструктора копирования в какой-либо произвольной функции foo f();   -  person C. Broadbent    schedule 26.11.2012
comment
f() всегда возвращает значение и никогда не возвращает ссылку rvalue. NRVO означает, что bar создается в правильном месте для возвращаемого значения. Если бар подвижен, то, если NRVO не может быть выполнено, возвращаемое значение - это перемещение, построенное из бара. Кажется, это то, что вы подразумеваете под «возвратом по r-значению ref», но это неточное описание. Если его нельзя переместить, то он построен по копии. Кажется, это то, что вы подразумеваете под «возвратом по значению», но это также неверно. Опять же, f() всегда возвращает значение, и внутренние детали того, как создается возвращаемое значение, не меняют этого.   -  person bames53    schedule 26.11.2012


Ответы (3)


компилятор попытается сделать: NRVO, возврат по r-значению ref, возврат по значению, сбой; в этой последовательности.

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

То есть, если ваш тип имеет конструктор перемещения, компилятор, использующий конструктор копирования, не будет совместим с C++11.

person David Rodríguez - dribeas    schedule 26.11.2012
comment
Из моего тестирования с g++ и clang, похоже, что он использует конструктор копирования, когда NRVO не работает. См. ideone.com/zoeixm. Только если вы делаете return std::move(...), он использует конструктор перемещения. Я не уверен, что это предусмотрено стандартом, или компиляторы просто недостаточно умны, чтобы справиться с этим случаем. - person hyperair; 22.03.2013
comment
Хорошо, похоже, что тернар был причиной проблем. Расширение return flag ? t : f; в if (flag) return t; else return f; заставило вместо этого использовать конструктор перемещения. Причина этого в том, что троичное выражение вместо этого приводит к B&. - person hyperair; 22.03.2013
comment
@hyperair: Причина на самом деле не в этом (t в return t; также является B&), хотя вы можете использовать это как напоминание, если это вам поможет. Настоящая причина в том, что компилятор не может выйти из lvalue вообще, с фиксированным набором исключений в стандарте, в частности, в операторе возврата, когда выражение - это имя энергонезависимого автоматического объекта (12.8/31). Смелое лицо принадлежит мне. В return t; выражением является имя, копию можно опустить, а если нет, то объект необходимо переместить. В return cond ? t : r; такого нет - person David Rodríguez - dribeas; 22.03.2013

Ваш код никогда не вернется путем копирования, если foo имеет работающий конструктор перемещения.

person Mankarse    schedule 26.11.2012

Вы также можете использовать параметр out вместо возврата:

void f(foo& bar) { ... }

Но на практике все компиляторы будут использовать NVRO.

person Andrew Tomazos    schedule 26.11.2012