Является ли преждевременной оптимизацией использование std:: move()?

Предположим, что у меня есть следующий код:

int main()
{
    std::vector<std::string> strs;

    std::string var("Hello World");
    // Make some modifications to 'var'
    strs.push_back(std::move(var));
}

Часть образца, которую я хочу указать, - это использование std::move(). В основном меня беспокоит копия на вызов push_back(). Предположим, что строка, которую я добавляю, действительно большая. Я все еще изучаю ссылки на С++ 11 r-value, поэтому я не уверен, как компилятор будет оптимизировать копию (если вообще) без std::move().

Может ли кто-нибудь объяснить, является ли это преждевременной оптимизацией (форсирование ходов во всех случаях, когда вы хотите вообще избежать копий)? Если да, то какие шаблоны я должен ожидать, что компилятор будет следовать (или, скорее всего, следовать), что приведет к оптимизации и автоматическому перемещению здесь?

ИЗМЕНИТЬ

Я хочу добавить, что я понимаю, как происходит автоматическое перемещение по возвращаемым значениям функции, поскольку применяется NRVO/RVO. В конкретном примере, который я привел здесь, не будет применяться RVO, поэтому я не уверен.

Ответ 1

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

Короче: Нет, использование "трюка", такого как std:: move, как описано в вопросе, не является преждевременной оптимизацией. Не использовать std::move, когда он может быть использован, тоже хорошо, если только код не известен как критически важный.

Необходимость избегать преждевременной оптимизации иногда понимается как "не оптимизируйте, пока не сможете это доказать", но я предпочитаю читать ее как "не тратьте время на решение проблем, если вы не знаете, что они должны быть решена".

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

Связанный пример: люди, работающие в критическом критически важном коде, часто передают аргументы как ссылки const (const std::string&). Из-за того, что они привыкли делать, они будут использовать один и тот же шаблон в коде, который не критичен по производительности, хотя они могут просто использовать pass-by-copy (const std::string или даже std::string). Это не является преждевременной оптимизацией.

Ответ 2

Я не уверен, как компилятор будет оптимизировать копию (если вообще) без std::move().

Только очень умный компилятор может оптимизировать это, поэтому, если копия может быть дорогой (например, очень длинная строка), тогда лучше ее переместить.

Без перемещения код фактически представляет собой последовательность вызовов:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector
malloc  // allocate new string
strcpy  // copy from var to new string
free    // destroy var

При перемещении он становится:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector

Теоретически интеллектуальный компилятор может сделать это преобразование автоматически, но для компилятора для просмотра всех уровней абстракции, введенных конструкторами и деструкторами, и функций-членов vector достаточно сложно, поэтому доказательство того, что код может быть преобразованный для удаления a malloc и free является сложным.

Ответ 3

После std::move исходный объект в этом случае var должен находиться в допустимом состоянии, но может содержать любое значение, например. он может быть пустым.

Если вы знаете, что не собираетесь использовать var, и вы создали его только в vector, то это не настоящая "преждевременная" оптимизация, как намерение того, что вы пытаетесь сделать.

vector имеет новый метод emplace_back(), который является тем же самым, но понятным, и использует форвардные аргументы (здесь вы просто делаете emplace_back("Hello World"), если все, что вы делаете, это его создание). В вашем случае, поскольку вы "вносите некоторые изменения с var" emplace_back, вряд ли будет уместным.

В старой C++ вы можете оптимизировать копию, выполнив push_back() на пустой строке, затем заменив ее.

Ответ 4

Я не знаю, почему все предлагают использовать emplace_back(). Цель emplace_back() состоит в том, чтобы избежать операций копирования/перемещения, построив объект на месте. В этом случае вы уже создали объект, поэтому по крайней мере 1 копия/перемещение неизбежно. В этом случае нет преимущества использовать emplace_back() более push_back().

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

Ответ 5

Да, это преждевременная оптимизация, если это преждевременная оптимизация.

Позвольте мне уточнить:
Преждевременная оптимизация определяется тем, оптимизируете ли вы часть кода, который, как вы не знаете, критичны по производительности. То есть преждевременная оптимизация никогда не определяется методом оптимизации, она всегда определяется местом оптимизации и вашими знаниями о том, что вы делаете.


Теперь, чтобы оптимизировать с помощью std::move():

С std::move() вы избегаете тяжелого подъема конструкции копии, например, выделения памяти, освобождения, копирования элементов, содержащих элементы, деконструирования содержащихся элементов и т.д. Это хорошо. Но одна часть решает: строительство/уничтожение контейнерного объекта.

emplace_back() имеет цель полностью избежать создания временного объекта. Итак, всякий раз, когда вы можете избежать временного объекта с помощью emplace_back(), у вас есть небольшая победа.

Учитывая качество кода (читаемость/ремонтопригодность): std::move() имеет недостаток, оставляя вас с непригодным для использования объектом, который может быть источником ошибок. Этого не происходит с emplace_back(). Так что, опять же, более очевидно, что предпочтительнее.

Конечно, emplace_back() не может использоваться во всех контекстах, которые позволяют использовать std::move(). Таким образом, если они критичны по производительности, std::move() может быть способом. Есть несколько очень правильных вариантов использования для оптимизации с помощью std::move(). Тем не менее, вы также можете узнать, что вы можете написать код таким образом, чтобы не требовать каких-либо конструкций copy/move/emplace. Никогда не переставайте искать оптимальное решение при оптимизации!

В конце концов, остается, что правильность оптимизации с помощью std::move() полностью зависит от контекста: хорошая оптимизация там, где это хорошая оптимизация.