C ++ 0x стиль кодирования лямбда-выражений - PullRequest
22 голосов
/ 30 июля 2009

Интересно, как люди используют лямбды C ++ 0x, с точки зрения стиля кодирования. Самый интересный вопрос заключается в том, насколько тщательным должно быть составление списка захвата. С одной стороны, язык позволяет явным образом перечислять захваченные переменные, и поэтому «явное лучше, чем неявное правило», поэтому имеет смысл составить исчерпывающий список, чтобы четко указать намерение. E.g.:

 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 });

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

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

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

Что ты думаешь?

Ответы [ 5 ]

6 голосов
/ 30 июля 2009

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

На самом деле я еще не использовал лямбды C ++ (кроме того, чтобы возиться с бета-версией VC10), но большую часть времени я бы использовал с последним

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

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

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

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

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

2 голосов
/ 30 июля 2009

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

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

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

0 голосов
/ 03 августа 2009

Я читаю следующую ссылку, чтобы лучше понять лямбду C ++. Стиль кодирования, использованный в примерах, довольно изящен, и я могу следовать: http://uint32t.blogspot.com/2009/05/using-c0x-lambda-to-replace-boost-bind.html

0 голосов
/ 02 августа 2009

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

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

0 голосов
/ 30 июля 2009

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

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

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

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

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

...