Подтвердить что ты не робот

С++ 0x стиль кодирования lambdas

Интересно, как люди используют С++ 0x lambdas с точки зрения стиля кодирования. Самый интересный вопрос - насколько тщательным является при записи списка захвата. С одной стороны, язык позволяет явно отображать захваченные переменные, а "явным лучше, чем неявное правило", поэтому имеет смысл сделать исчерпывающий список, чтобы четко указать интуицию. Например:.

 int sum;
 std::for_each(xs.begin(), xs.end(), [&sum](int x) { sum += x });

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

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

 int sum;
 std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

Очевидно, что это не должно быть ничего или ничего, но должно быть какое-то обоснование, чтобы решить, когда делать автоматический захват, и когда делать захват явно. Любые мысли?

Другой вопрос в том же ключе - когда использовать capture-by-copy - [=], а когда использовать capture-by-reference - [&]. Захват за копией, очевидно, более безопасен, потому что нет проблем с продолжительностью жизни, поэтому можно утверждать, что он должен использоваться по умолчанию, когда нет необходимости мутировать захваченное значение (или видеть изменения, сделанные с ним из других источников) по-ссылке следует рассматривать как (потенциально преждевременную) оптимизацию в таких случаях, применять только там, где это явно имеет значение.

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

Каковы ваши мысли?

4b9b3361

Ответ 1

Я никогда не слышал о правиле "явное лучше, чем неявное правило", и я не согласен с ним. Бывают случаи, когда это правда, конечно, но и множество случаев, когда это не так. Вот почему 0x добавляет вывод типа с ключевым словом auto. (и почему параметры шаблона функции уже выведены, когда это возможно). Есть много случаев, когда неявный предпочтительнее.

Я еще не использовал С++ lambdas (кроме того, что выкарабкался с помощью бета-версии VC10), но я бы чаще всего ездил с ним

std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

Мое рассуждение? Почему бы не сделать это? Это удобно. Оно работает. И это легче поддерживать. Мне не нужно обновлять список захвата, когда я изменяю тело лямбда. Почему я должен быть явным в отношении того, что компилятор знает лучше меня? Компилятор может определить список захвата на основе того, что фактически используется.

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

Это не похоже на выбор, который вы должны сделать при разработке регулярной функции.

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

Ответ 2

Мой первоначальный инстинкт заключался в том, что захват по значению предлагает более или менее то же, что и анонимные внутренние классы Java, которые являются известным количеством. Но вместо того, чтобы использовать трюк array-of-size-1, когда вы хотите, чтобы охватывающая область изменялась, вы можете вместо этого захватить по ссылке. Тогда ваша ответственность ограничить продолжительность лямбда в пределах сферы действия referand.

На практике я согласен с вами в том, что захват по ссылке должен быть дефолтом при работе с алгоритмами, которые, как я ожидаю, будут использоваться большинством. Общее использование для анонимных внутренних классов в Java - это слушатели. С самого начала на С++ существует меньше интерфейсов в стиле слушателя, поэтому это меньше, но все же там. Лучше всего строго придерживаться значения в таком случае, чтобы избежать возможности для ошибки. Может ли захват по значению shared_ptr быть большой идиомой, может быть?

Однако я еще не использовал лямбда, поэтому я вполне мог пропустить что-то огромное.

Ответ 3

Я вижу новое стандартное правило кодирования здесь!;)

Это немного надуманное, но для того, чтобы выделить "преимущество" для явного, рассмотрим следующее:

void foo (std::vector<int> v, int x1)
{
  int sum = 0;
  std::for_each (v.begin ()
    , v.end ()
    , [&](int xl) { sum += x1; } 
}

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

В очень строгой среде (критическая безопасность) я вижу, что такое правило является частью стандарта кодирования.

Ответ 4

Я бы пошел с явным списком захвата, когда это было бы удобно, когда вы захотите захватить множество переменных тогда (вы, вероятно, что-то делаете неправильно), вы можете использовать захват всех [&] списка захвата.

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