Это, наверное, простой вопрос, но это натолкнулось на мои мысли. Речь идет о различии между двумя функциями ниже:
T func_one(T obj) { //for the purpose of this question,
return obj + obj; //T is a large object and has an overloaded '+' operator
}
T func_two(T obj) {
T output = obj + obj;
return output;
}
В func_one()
вместо создания объекта T
, присваивая ему значение, а затем возвращая объект, я просто возвращаю значение без создания нового объекта. Если T
был большим объектом, func_one()
бы более эффективным, чем func_two()
или func_one()
делает объект T
любом случае при возврате суммы двух объектов?
Современные компиляторы могут преобразовать версию с дополнительной переменной в единицу без (названная оптимизация возвращаемого значения, это довольно частый источник вопросов здесь, на SO, почему не вызывает конструктор-копию при возврате LOCAL-переменной, например). Так что это не накладные расходы, о которых вы должны беспокоиться.
Накладные расходы, о которых вы должны беспокоиться, - это служебные вызовы функции. Добавление занимает современный процессор не более одного цикла. Вызов функции занимает от 10 до 20 циклов, в зависимости от количества аргументов.
Я немного сомневаюсь, что вы имеете в виду с T
в своем вопросе (это параметр шаблона? Это класс?). Это заполнитель для типа, который вы не хотели раскрывать в своем вопросе?). Однако вопрос о том, есть ли у вас проблема с функцией вызова функции, зависит от этого типа. И это зависит от того, может ли ваш компилятор встроить вашу функцию.
T
- сложный тип с дорогостоящей operator+()
, то вы тоже прекрасны.T
int
, например, и ваша функция не включена, то у вас есть примерно 90% накладных расходов в вашей функции.Краткий ответ: мы не можем знать
Длинный ответ: он сильно зависит от того, как работает T, и ваши компиляторы поддерживают оптимизацию возвращаемого значения.
Любая функция, которая возвращает значение, может использовать RVO или NRVO-оптимизацию. Это означает, что он построит возвращаемое значение непосредственно в вызывающей функции, исключив конструктор копирования. Поскольку это проблема с возвратом больших объектов по значению, это будет означать существенное повышение производительности.
Разница между func_one и func_two заключается в том, что func_one возвращает анонимное временное значение, значение r; это означает, что RVO можно тривиально использовать. func_two возвращает именованное значение, значение l, поэтому будет использоваться более сложная оптимизация NRVO. Однако func_two тривиально, поэтому почти наверняка будет применено NRVO, и обе функции будут в основном идентичными.
Предполагается, что у вас есть современный или даже полусовременный компилятор; если нет, это будет сильно зависеть от того, как вы реализовали T.
Если T имеет семантику перемещения, ваш компилятор вместо этого сможет перемещаться, а не копировать. Это должно относиться к обеим функциям, поскольку временные существуют в обоих случаях; однако, поскольку func_two возвращает именованное значение, возможно, он не сможет использовать семантику перемещения. Это до компилятора, и если компилятор не выполняет RVO или NRVO, я сомневаюсь, что он делает ход.
Наконец, это зависит от того, как реализуется оператор + operator и =. Если, например, они были реализованы в виде шаблонов выражений, то fun_two все еще требует назначения, что замедлит его, когда func_one просто вернет очень оптимизированное временное.
В резюме Почти во всех практических контекстах они идентичны. В исчезающем маленьком окне, где ваш компилятор действует очень странно, func_one почти повсеместно быстрее.
Компилятор оптимизировал бы fund_two во что-то похожее на func_one, которое затем было бы оптимизировано для чего-то еще, короткое короткое, вам не нужно беспокоиться об этом, если вам действительно не нужно об этом беспокоиться, тогда в этом случае вы можете посмотреть на выход asm.