Давайте предположим, что у меня есть три функции f0, f1, f2
что все принимают три аргумента. Давайте также предположим, что у меня есть большая функция bigFunc
вызвать, который принимает возвращаемые значения из первых трех функций в качестве аргументов.
Я могу хотеть сделать звонок как это:
bigFunc(f0(arg0, arg1, arg2), f1(arg3, arg4, arg5), f2(arg6, arg7, arg8));
Это большой вызов, поэтому я думаю, что было бы гораздо удобнее написать что-то вроде этого:
auto bigArg0 = f0(arg0, arg1, arg2);
auto bigArg1 = f1(arg3, arg4, arg5);
auto bigArg2 = f2(arg6, arg7, arg8);
bigFunc(bigArg0, bigArg1, bigArg2);
Это особенно здорово, если имена bigArg0, bigArg1, bigArg2
позвольте мне быть более конкретным о том, что я делаю (например, если f0, f1, f2
немного общего; Вы можете подумать об алгоритмах STL, которые делают разные вещи в зависимости от типа итераторов, которые вы ему предоставляете).
Однако проблема заключается в том, что bigArg0, bigArg1, bigArg2
Я заставляю их больше не быть временными, что компилятору (я полагаю) труднее оптимизировать.
Вот мой вопрос: Как правильно это сделать, если я не хочу терять производительность? делать bigArg0, bigArg1, bigArg2
Const? Приведите аргументы bigFunc
через std::move
? Немного и того, и другого?
Имеют ли деструктор и конструктор копирования возвращаемых значений наблюдаемое поведение, как их определяет стандарт?
Если они этого не делают, и у компилятора есть вся необходимая информация, чтобы доказать это, компилятор может свернуть копию без необходимости исключения RVO.
Возможно, это тот случай, если вы используете std::move
? В любом случае перемещение может быть лучше, чем копирование …
Или возможно bigfunc
получает свои аргументы с помощью константной ссылки, в этом случае оба пути приводят к одному и тому же коду в любом случае …
Вот решение, которое позволяет избежать именованных временных:
bigFunc(
f0(arg0, arg1, arg2),
f1(arg3, arg4, arg5),
f2(arg6, arg7, arg8)
);
Пример в увеличенном масштабе:
bigFunc(
f0(
f0_0(arg0, arg1, arg2),
f0_1(arg3, arg4, arg5),
f0_2(arg6, arg7, arg8)
),
f1(
f1_0(arg9, arg10, arg11),
f1_1(arg12, arg13, arg14),
f1_2(arg15, arg16, arg17)
),
f2(
f2_0(arg18, arg19, arg20),
f2_1(arg21, arg22, arg23),
f2_2(arg24, arg25, arg26)
)
);
Это обрабатывает 27 аргументов и 13 вызовов функций в 17 легко читаемых строках.
Это уже много дел в одном месте. К тому времени, когда это масштабируется настолько, что теряет читабельность, вы должны начать помещать его части в отдельные функции и / или объединять аргументы в структуры или классы.