Как избежать параметров?

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

Часть первая моего вопроса: Что такое часть Вашего фаворита/распространенных способов того, чтобы обходить использование параметр? Материал вдоль строк: Человек, в экспертных оценках, я всегда вижу, что другие программисты делают это, когда они, возможно, легко сделали это этот путь.

Часть вторая моего вопроса имеет дело с некоторыми конкретными случаями, с которыми я встретился, где я хотел бы избежать параметр, но не могу думать об очевидном способе, чтобы сделать так.

Пример 1: у Меня есть класс с дорогой копией, которой я хотел бы избежать. Работа может быть сделана на объекте, и это создает объект быть дорогим для копирования. Работа для создания данных не точно тривиальна также. В настоящее время я передам этот объект в функцию, которая изменит состояние объекта. Это мне предпочтительно для new'ing объект, внутренний к функции рабочего и возврату его назад, поскольку это позволяет мне сохранять вещи на стеке.

class ExpensiveCopy //Defines some interface I can't change.
{
public:
    ExpensiveCopy(const ExpensiveCopy toCopy){ /*Ouch! This hurts.*/ };
    ExpensiveCopy& operator=(const ExpensiveCopy& toCopy){/*Ouch! This hurts.*/};

    void addToData(SomeData);
    SomeData getData();
}

class B
{
public:
    static void doWork(ExpensiveCopy& ec_out, int someParam);
    //or
    // Your Function Here.
}

Используя мою функцию, я получаю код вызова как это:

const int SOME_PARAM = 5;
ExpensiveCopy toModify;
B::doWork(toModify, SOME_PARAM);

Я хотел бы иметь что-то вроде этого:

ExpensiveCopy theResult = B::doWork(SOME_PARAM);

Но я не знаю, возможно ли это.

Второй Пример: у Меня есть массив объектов. Объекты в массиве являются составным типом, и я должен сделать работу над каждым элементом, работу, что я хотел бы сохранить разделенным от основного цикла, это получает доступ к каждому элементу. Код в настоящее время похож на это:

std::vector<ComplexType> theCollection;
for(int index = 0; index < theCollection.size(); ++index)
{
    doWork(theCollection[index]);
}

void doWork(ComplexType& ct_out)
{
   //Do work on the individual element.
}

Какие-либо предложения о том, как иметь дело с некоторыми из этих ситуаций? Я работаю, прежде всего, в C++, но мне интересно видеть, упрощают ли другие языки более легкую установку. Я встретился с RVO как с возможным решением, но я должен читать больше на нем, и он походит на компилятор определенная функция.

8
задан FP. 26 January 2010 в 16:34
поделиться

9 ответов

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

Код

static void doWork(ExpensiveCopy& ec_out, int someParam);

отлично выглядит хорошо для меня.

Если вы действительно хотите изменить его, то у вас есть пара параметров

  1. Переместить Dowork, так что это член ExpensiveCopy (который вы говорите, вы не можете сделать, так что это)
  2. вернуть (Smart) Указатель из Dowork вместо того, чтобы копировать его. (который вы не хотите делать, как вы хотите сохранить вещи на стеке)
  3. полагаться на RVO (что указывало другие, поддерживаются в значительной степени все современные компиляторы)
7
ответ дан 5 December 2019 в 11:24
поделиться

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

Expensive work() {
    // ... no branched returns here
    return Expensive(foo);
}

Expensive e = work();

В некоторых случаях компиляторы могут применять NRVO, называемую оптимизацией возвращаемого значения, также:

Expensive work() {
    Expensive e; // named object
    // ... no branched returns here
    return e; // return named object
}

Это, однако, не совсем надежно, работает только в более тривиальных случаях и должно быть протестировано. Если вы не готовы тестировать каждый случай, просто используйте out-параметры со ссылками во втором случае.

3
ответ дан 5 December 2019 в 11:24
поделиться

ИМО первое, что вы должны спросить, является ли копирование ExpensevecopeCypeCypeCOry настолько запрещенным дорогим. И ответить на это, вам обычно нужен профилировщик. Если Profiler не говорит вам, что копирование действительно является узким местом, просто напишите код, который легче читать: EXPENSIVECOPY OBJ = Dowork (Param); .

Конечно, существуют действительно случаи, когда объекты не могут быть скопированы на производительность или другие причины. Затем Ответ Нила применяется.

2
ответ дан 5 December 2019 в 11:24
поделиться

В дополнение ко всем комментариям здесь я бы упомянул, что на C++0x выходной параметр редко используется для оптимизации -- из-за конструкторов движения (см. здесь)

.
2
ответ дан 5 December 2019 в 11:24
поделиться

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

0
ответ дан 5 December 2019 в 11:24
поделиться

Над какой платформой вы работаете?

Причина, по которой я спрашиваю, заключается в том, что многие люди предложили оптимизацию Return Value Optimization, которая является очень удобной оптимизацией компилятора, присутствующей почти в каждом компиляторе. Кроме того, Microsoft и Intel реализуют то, что они называют "Оптимизация по имени" (Named Return Value Optimization), что еще более удобно.

В стандартной функции Return Value Optimization оператор возврата представляет собой вызов конструктора объекта, который сообщает компилятору об устранении временных значений (необязательно операции копирования).

В опции Named Return Value Optimization (Оптимизация по имени возвращаемого значения) вы можете вернуть значение по его имени, и компилятор сделает то же самое. Преимущество NRVO состоит в том, что перед возвращением можно выполнять более сложные операции над созданным значением (например, вызывать на нем функции).

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

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

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

0
ответ дан 5 December 2019 в 11:24
поделиться

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

Простой факт в том, что некоторые функции должны возвращать несколько вещей, и в большинстве языков это предполагает наличие параметров. Common Lisp имеет multiple-value-bind и multiple-value-return, в которых список символов предоставляется связкой и возвращается список значений. В некоторых случаях функция может возвращать составное значение, например, список значений, которые затем будут деконструированы, и для функции C++ это не так уж и важно, чтобы она возвращала пару std::pair. Возвращение более двух значений в Си++ таким образом становится неудобным. Всегда можно определить структуру, но определение и создание ее часто будет более запутанным, чем параметры out.

В некоторых случаях возвращаемое значение перегружается. В C getchar() возвращает int, идея в том, что значений int больше, чем char (правда во всех известных мне реализациях, ложь в некоторых легко представить), поэтому одно из значений может быть использовано для обозначения конца файла. atoi() возвращает целое число, либо целое, представленное переданной строкой, либо ноль, если его нет, поэтому оно возвращает одно и то же для "0" и "лягушка". (Если вы хотите узнать, было ли значение int или нет, используйте strtol(), который имеет параметр out.)

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

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

0
ответ дан 5 December 2019 в 11:24
поделиться

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

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

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

0
ответ дан 5 December 2019 в 11:24
поделиться

Если вы не спуститесь с «все неизменяемым» маршрутом, который не сидит слишком хорошо с C ++. Вы не можете легко избежать параметров. Стандартная библиотека C ++ использует их, а что достаточно хорошего для этого, достаточно для меня.

1
ответ дан 5 December 2019 в 11:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: