C++NRVO гарантирует? Или лучше предпочесть неконстантный параметр ref или shared_ptr?

Я использую C++ с 1992 года (и много читал об этом языке), так что знаю об этом языке довольно много, но далеко не все. Мой вопрос касается оптимизации именованного возвращаемого значения С++ 11 - какие гарантии, что она будет выполнена? Я предпочитаю отправлять неконстантные параметры (стиль C++97) или использовать shared_ptr (стиль C++11) или даже использовать ptr-to-ptr (стиль C). Одна из причин заключается в том, что с неконстантными аргументами ref или shared_ptr мне гарантируется, что НИКАКИХ дополнительных копий объекта не будет.

Итак, мой вопрос (особенно для тех программистов на C++, которые занимаются тяжелой работой в реальном времени или ядром): какие идиомы вы предпочитаете? Я ДЕЙСТВИТЕЛЬНО надеюсь, что этот вопрос не будет отвергнут как неточный, основанный на мнениях или просто глупый - я знаю, что он очень важен для эффективного современного программирования на С++.


person Erik Alapää    schedule 13.03.2015    source источник
comment
RVO — это не C++11. И C++97 не вещь. Но RVO был частью языка с момента его стандартизации (C++98). Я предпочитаю возвращать по значению. Меньше вещей для размышлений/документов.   -  person juanchopanza    schedule 13.03.2015
comment
Лично я передаю параметры по константной ссылке, если они не должны быть изменены, по указателю, если они могут быть изменены (я чувствую, что вызывающей стороне будет понятнее, если это будет сделано таким образом). Я склонен возвращать вещи по значению, полагаясь на то, что компилятор пропускает копии или предоставляет конструктор перемещения, если у меня есть какие-либо опасения, что компилятор может этого не делать. Единственная новая вещь здесь — конструктор перемещения.   -  person Bathsheba    schedule 13.03.2015
comment
Жестких гарантий нет, стандарт позволяет реализациям выполнять RVO в определенных случаях ([class.copy] / 31), но реализация может никогда не использовать RVO и по-прежнему соответствовать требованиям.   -  person user657267    schedule 13.03.2015
comment
Я бы сказал, сначала положиться на него. Доверяйте компилятору, не пытайтесь помочь ему оптимизировать ваш код. А потом мерить. Если вы заметите, что оптимизация с NRVO кажется неэффективной и вам это мешает, то переходите к ручному решению.   -  person JBL    schedule 13.03.2015
comment
@juanchopanza: комитет по стандартам единогласно одобрил стандарт C++ в ноябре 1997 года, поэтому я назвал стандарт C++97, но, вероятно, C++98 более правильный. А для NRVO, как я понимаю, это C++11 модификация старых правил.   -  person Erik Alapää    schedule 13.03.2015
comment
NRVO определенно предшествовал C++11, а IIRC уже был в C++98.   -  person juanchopanza    schedule 13.03.2015
comment
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что какие идиомы вы предпочитаете? спрашивает мнения, и введение, которое я склонен предпочитать, подкрепляет это. NVRO, безусловно, имеет отношение к C++ и может повлиять на производительность, но это не делает все вопросы NVRO актуальными.   -  person MSalters    schedule 13.03.2015
comment
@MSalters: Если вам не нравятся серьезные вопросы от работающих программистов о реальных вариантах дизайна, просто прочитайте вопрос и переходите к тому, что вам нравится. Голосование за закрытие является агрессивным и снисходительным, и его следует использовать для устранения вопросов и людей, которые несерьезны и неискренни, а не для демонстрации силы в Stackoverflow.   -  person Erik Alapää    schedule 13.03.2015
comment
@juanchopanza: По крайней мере, похоже, что в правила NRVO/RVO были внесены изменения из-за C++11, это из другого вопроса в SO: «Плюс, когда вы возвращаете локальную переменную, она перемещается, если она имеет значение тип, у которого есть конструктор перемещения, поэтому копии не будут делаться, и точка.   -  person Erik Alapää    schedule 13.03.2015
comment
Это неправильно. Цитата либо вводит в заблуждение, либо ошибочна, либо вырвана из контекста. Он перемещается только если копия не удаляется. Я не думаю, что правила NRVO были изменены в С++ 11.   -  person juanchopanza    schedule 13.03.2015
comment
Вот обсуждение; stackoverflow.com/questions/6531700/ И совершенно очевидно, что такая радикально новая функция языка, как ссылки rvalue и конструкторы перемещения, повлияет на оптимизацию возвращаемого значения. сделано, поэтому я думаю, что вы ошибаетесь в этом. Но опять же, я предпочитаю интеллектуальные указатели или другие механизмы, а не RVO/NRVO.   -  person Erik Alapää    schedule 14.03.2015
comment
При исследовании RVO я освежил свои знания о ссылках на rvalue. Их сложно понять, но они помогают писать еще более быстрые программы на C++. Например, мне нравится новый std::swap() со ссылками на rvalue и std::move. Но иногда вещи становятся слишком волосатыми, на мой вкус, см., например. правила свертывания ссылок в thbecker.net/articles/rvalue_references/section_08.html — вещи как это заставляет меня вернуться к передаче указателей в стиле C.   -  person Erik Alapää    schedule 16.03.2015
comment
Свертывание ссылок @ErikAlapää — это единственное, о чем вам нужно беспокоиться, когда вы понимаете переадресацию ссылок (чья полезность перевешивает их уродство).   -  person M.M    schedule 24.07.2015
comment
В случае, если вы не знаете: пока ваш класс действительно имеет конструктор перемещения, компилятор должен использовать его, если он решит не выполнять копирование.   -  person M.M    schedule 24.07.2015


Ответы (1)


В Разделе 12.8/31 стандарт C++11 пишет

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

Это означает, что ваш компилятор может никогда не использовать RVO (хотя большинство компиляторов поддерживают его).

Учитывая вышеизложенное, совет Скотта Мейерса из «Effective Modern C++» (статья 25)

«Никогда не применяйте std::move или std::forward к локальным объектам, если в противном случае они подходят для оптимизации возвращаемого значения».

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

  • Если вы делаете применение std::move, то будет использоваться конструктор перемещения (который дороже, чем RVO), даже если RVO был возможен. Таким образом, вы, возможно, теряете некоторую производительность.
  • Если вы не применяете std::move, вы оставляете место для RVO, если ваш компилятор поддерживает его. Если ваш компилятор не поддерживает RVO, вы все равно будете использовать конструктор перемещения. Таким образом, вы, возможно, получите некоторую производительность.

Clang будет иметь предупреждения -Wpessimizing-move и -Wredundant-move для этого. См. эту ссылку.

person Bart Vandewoestyne    schedule 23.07.2015