Pull to refresh

Comments 143

Впервые вижу чтобы код был больше заголовков
У меня в хабраредакторе не получается задать размеры =(
UFO just landed and posted this here
Тем не менее читать очень даже удобно, сам никогда не думал что код большим кеглем будет выглядеть нормально.
Имхо, для небольших кусочков кода это намного лучше нежели как обычно. А если простыня кода, то тогда конечно лучше по стардарту, так сказать :)
Вот и отлично. Подслеповатые вроде меня в кои-то веки могут без надрыва читать листинги.
У меня в MSVC такой же шрифт.
Так как лучше? Я их немного отресайзил…
Как-то [спорно], что Java «позволяет создавать налету локальные не именованные функции внутри выражений».
Это как? Только не говорите про анонимные классы. Класс — не функция.
Может, правильней будет просто удалить Java из списка (хотя я сам мечтаю, чтобы Java позволяла это… ну хотя бы к версии 8). JVM — не язык. А Scheme (по ссылке) — все-таки далеко не Java.
С каждым новым витком развития С++ его синтаксис становится всё страшнее и страшнее.
Я, конечно, пускаю скупую слезу когда вижу более читабельный C#-код, но к C++ и всем этим multiple inderections довольно легко привыкнуть, если этим заниматься, а не смотреть из-за угла.
Из-за угла — да, страшно.

Ну и представьте каково разбирать подобное адептам визуального бейсика.
Думаю вы правы, но в случае .NET: «зачем платить больше, если не видно разницы?»
А если отвлечься от x86 и посмотреть на другие платформы? C++ есть почти под всё (так как есть C), чего не скажешь про .Net.
C++ есть почти под всё, но почти под все он немного различается. Иначе не было бы https://developer.mozilla.org/En/C___Portability_Guide.
Согласен. Ну почему нельзя было сделать синтаксис, похожий хотя бы на Python?
Но это же очевидно — обратная совместимость. Если радикально изменить синтаксис C++ — это будет совсем другой язык. Python, D, или что-то ещё. К тому же, как было замечено выше, к синтаксису плюсов, при постоянной работе, довольно быстро привыкаешь. :)
Вот, вот. Судя по комментам ниже не во всех функциональных языках такой синтаксис на данную фичу… Интересно было знать зачем такой страшный синтаксис описания лямбда-выражений ввели. Может кто-то ссылочку кинет, а то кателок уже не варит после рабочего дня, а быстро нагуглить не удалось.
вероятно чтобы имеющийся код остался рабочим. использовали конструкции невозможные в предидущем стандарте
Насколько я предствляю, в с++ невозможны были и конструкции лямбда выражений из c#, так что могли бы сделать более вменяемый синтаксис.
А вообще эта погоня за обратной совместимостью и бесконечные костыли делают с++ просто нечитаемым.
Все равно, до перла ему далеко (:

ЗЫ: Перл я тоже люблю :)
Теперь фактически вот это валидный код на C++:

int main(){[](){}();}

Черт возьми, ниже такое уже написали :)
Этак скоро C++ превратится в Perl или что-то подобное…
:)))
UFO just landed and posted this here
так держать :p (http://artlung.com/smorgasborg/Invention_of_Cplusplus.shtml)
UFO just landed and posted this here
UFO just landed and posted this here
Это вы троллите так нетолсто, а я под вечер уже не могу различить, или всё на полном серьёзе?
UFO just landed and posted this here
Ну, GCC всего лишь один из компиляторов, а лямбда-функции добавили в стандарт С++, а не только в одну конкретную реализацию компилятора С++. А из вашего поста было не очевидно, что претензия именно к GCC.
UFO just landed and posted this here
C++ не императивный язык. И не объектно-ориентированный. Он мультипарадигменный. Кто как хочет, так и программирует. Никто ж не заставляет использовать все без исключения возможности языка. С другой стороны, если лямбда-функции в каких-то местах значительно сократят код и не будут являться ботлнеком, то даже с плохим (допустим) GCC — почему нет?
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
>чтобы было
Я подозреваю, что сделали потому, что это не так уж и тяжело сделать, да и не грех пополнить список оптимизаций.
UFO just landed and posted this here
>Когда-то Си был таким удобным, приятным языком
Ну кагбе каким он был таким он и остался
UFO just landed and posted this here
UFO just landed and posted this here
Хватит уже называть С++ Сями. Это разные языки.
UFO just landed and posted this here
"++" в названии — это не столько объектно-ориентированность, сколько препроцессор, если я ничего не путаю.
UFO just landed and posted this here
Человеку, поставившему минус: Просьба ознакомиться с пунктами 6.5.2 и 6.5.3 стандарта языка Си (ISO/IEC 9899:1990). Ну или проверить компилятором.
UFO just landed and posted this here
Впервые слышу о таком. В книге Кернигана и Ричи уже есть про ++ и --.
Все гораздо проще, название «С++»:
«С» — как дань предшественнику и основе языка;
"++" — как указание на усовершенствование.
А оператор ++ был и в Си.
Ты не понел, Си таким как был таким и остался, это Си++ мутирует. BTW си это тоже static scope язык, и сделать лексические замыкания не так сложно.
Да, конешно
int fact = [](int x) -> int
{
int accumulator = 1;
beginning:
if (x == 0) return accumulator;
else
{
accumulator *= x;
x -= 1;
goto beginning;
}
}(n);
вам не трудно пояснить?
… }(n) меня слегка ушибло
int fact = [](int x) -> int{...}(n)
То есть просто вызов этой лямбды
Передача n в лямбду в качестве аргумента, если я правильно понял.
:D не, так не интересно…
Ну, во-первых, не будем торопиться: из черновика C++0x выбросили концепты, а это была более долгожданная и всячески восхваляемая фича.

Во-вторых, тем, кто использовал Boost.Lambda, станет легче, и это хорошо. А для прочих ничего не изменится :)
Согласен. Жалко, что их убрали =(
По по-поводу того что для других ничего не изменится должен не согласится. Раз появляются дополнения к синтаксису значит их надо знать, как минимум для собеседований.
А язык и так громадный с множеством тонкостей. Лучше уж библиотека чем такие рывки.
Не используйте лямбды, не используйте исключения, не используйте шаблоны. Пишите себе на «С с классами», кто вам мешает? Его никто никуда не забирал.
как выглядит передача лямбда функции — обьявление «принимающей» стороны?
Про второй не уверен, будет оно или нет.
std::tr1::function
std::tr1::reference_closure // ?
T // template <class T>
в качестве примера
std::tr1::function<void (int)> print = [](int n) { std::cout << n; };
теперь в C++0x легальным стало выражением:
[](){}();
прошу прощения за опечатку «выражение».
Ну вообще те, кто говорят о том, что «не хотите — не используйте» в каком-то смысле правы. Действительно не понятно, на кой именно эта фича нужна (мне она нахрен не сдалась). Но вот мне необходимы шаблоны с переменным числом параметров, но я больше чем уверен, что найдутся недовольные, которые будут орать о том, что введение данной фичи только испаганит язык… Так что тут бабушка на двое сказала и что действительно хорошо, а что не очень — нужно много и основательно думать.
Хрен знает насчет С++ — давно ничего сложного на нем не писал, но в других языках постоянно юзаю
насчет орущий +пятьсот, каждый хитрый выверт таких порождает, потому как въезжать в это нужно. Часто материал подается в руководствах по новым фичам через жопу, потому как авторы манула еще сами не поняли на кой оно им. Последние курсы майкрософта вобще жесть сплошная.
По моему вводить что-то новое всё таки нужно, но при этих нововведениях главное как можно меньше похерить того, что уже было. И если нововведение не затрагивает то, что существовало до него или сказывается очень несильно, особенно для таких столпов как C++, то я скорее за нововведения. Ну вот как пример в лямбда-выражениями: действительно такой несколько извратный синтаксис ввели, чтобы наверное не похерить ту идеологию в написания кода, которая сложилась до этого и существовала без лямбда-выражений. Главное ещё в этом стремлении «не похерить» совсем уж в крайность не впадать и если уж что-то вводить, то не просто некий набор того, чего до этого не было, а подложить под это дело хоть какую-то логику.
UFO just landed and posted this here
ну да, там так и написано — 0х :)
Прочитал внимательнее, запутался. Во-первых, какой тип получается у этих лямбда-выражений? Они вообще кто? Объекты? Если я хочу его в переменную сохранить, то что писать надо?
Откуда берутся переменные не объявленные в скобках? Из текущего контекста, из контекста применения выражения?

> Также лямбда выражение может принимать в качестве параметра другое лямбда выражение. С другой стороны, список параметров является опциональным элементов, и можно писать выражение, не включая его.
Неплохо бы уточнить, что результат скорее всего будет отличаться. Выражения [=]{return x;} и [=](int x){return x;} вообще-то разные вещи (надеюсь :))
1)[=]{return x;} и [=](int x){return x;}
Да, будет отличаться! В первом случае переменная «x» будет взята из контекста, второй вариант вернет входной параметр
Я к тому, что это надо отразить в статье, т.к. из того примера, что там, разница не видна
Я собирался написать статью про лямбды в C++, но меня опередили. Пожалуй, я все-таки напишу, ибо эта недостаточно подробная.
Да, тема здесь раскрыта не достаточно. Нужно подробнее.
Мне кажется, что «хорошим кодом» считается тот, который понятен многим.
А код «о, смотри, какую я штуку написал! угадай, что оно делает?» — плохой код, потому что он отнимает уйму человекочасов на поддержке.
Не сильно понимаю, как конструкция из множества скобок и спецсимволов (которую большинство постарается ещё и в одну строку записать) поможет «сделать код лучше»? Примером «как не стоит писать»?

PS: функция inc() случаем не ноль всегда возвращать будет? Кажется, там логическая ошибочка с местом инициализации переменной value ;)
вот видите, даже без лямбд код вы не поняли ;)
почитайте на досуге, что означает модификатор static
Я то как раз понял, А Вы? А запустить код пробовали?

// test.cpp, code begin
#include using namespace std;

int inc() {
static int z = 0;
return z++;
}

int main(void) {
for (int i = 0; i < 5; i++) {
cout << int() << endl;
}
return 0;
}
// code end

~$ g++ test.cpp && ./a.out
0
0
0
0
0
~$

Думайте, прежде чем минусовать
Прошу прощения, ступил. «int()» вместо «inc()» использовал, а статики из другой оперы вспомнил
C++ уже давно вышел из той стадии (а может никогда в ней и не был), когда код был понятен многим. С++ — сложный язык с высоким порогом вхождения, это никто не скрывает и не стремится исправлять.
Ну «начала» у этого языка не сложнее чем у других языков программироваия. Но вот наличие разных удобных фичек, о которых узнаёшь постоянно в процессе использования языка, меня лично радует несказанно :) Тот же static внутри функции, про который зашёл разговор — классная весчть, которая, к сожалению, конечно непонятна человеку, который только недавно начал программить на этом языке (сам наступал на эти грабли со static, когда не мог понять как это работает, правда даааавно это было). Язычок конечно посложнее многих _современных_ будет и глупости всвязи с его достаточно большой универсальностью можно понаделать массу. Но зато всегда есть куда развиваться — мне как разработчику и языку как языку, чтобы постараться сгладить некоторые острые углы.

В какой-то книге встречал фразу чувака, который давно программит на нём и уже может считаться гуру (не дословно, но смысл передам): «Я программлю на C++ уже N лет, но даже с моим опытом я делаю по крайней мере одну глупость каждый день»
Хм. И у Вас static внутри функций особенный какой-то? Начинаю сомневаться уже.
А что за версия C++ с «волшебными статиками»?
Прошу прощения, ступил. «int()» вместо «inc()» использовал, а статики из другой оперы вспомнил
Ну static в C++ действительно имеет довольно разные назначения:

1. для переменных внутри функции (как раз то, что в примере) он означает, что инициализация переменной осуществляется только при первом вхождении в функцию, а для всех последующих вхождений используется значение полученное на предыдущем вхождении

2. для переменных в классах это означает, что эта переменная одна для всех объектов и наследников и меняя значение этой переменной для одного объекта оно меняется для всех объектов этого (или наследованного) типа. Применение: подсчёт указателей на память, например.

3. переменных и функций, объявленных глобально с cpp-файле это означает, что область видимости этой переменной ограничена только этим файлом.

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

Ну вот то немногое, что просто «на коленке» вспомнил особо не задумываясь. Потому " static is a miracle !" :)
Я немножко не в курсе, поясните плиз:
* Инициализировать static-поле при декларации (случай 2 в Вашем списке) разве нельзя? Если можно — то он идентичен случаю 1

* В случае 3 — слово static делает объявленя приватными? Замысловато, однако. А почему в стандарте для этого не предлагается слово типа private?

* 4-ый случай — это как в Delphi объявление методов класса с помощью слова class? Интересно, почему тут оно объявляется словом static? Или они имеют ввиду, что метод — это типа ссылка на функцию, делают эту ссылку независимой от экземпляра, «статичной»?

* В фразе «приватную или протектную ты объявить где угодно не сможешь» — тут Вы имели ввиду «вызвать» а не «объявить», или я что-то не понял?
static-член класса может быть проинициализирован при декларации только если он const и является интегральным типом либо перечислением. Хотя многие компиляторы позволяют это делать и в других случаях, это отклонение от стандарта.

В случае 3 static функции и переменные не являются членами какого-то класса, static ограничивает область доступности этих переменных и функций текущей единицей трансляции.

Потому что эти методы могут вызываться без создания экземпляра класса (к статическим членам-данным тоже можно обращаться без создания экземпляра), кроме того, они могут доступаться к любым статическим (в том числе и приватным) членам-данным класса.
Кое что узнал новое из твоего коммента. А именно что можно спокойно инициализировать статическую переменную при объявлении внутри класса, если она констрантна. Быстренько накатал программульку, скомпилил, работает! Никогда в голову не приходило, хотя программлю на C++ уже не первый день. Как говорится: «Век живи — век учись». Спасибо.
Хе, меня очень радует что это вводят… иногда не хватает лямбд, а с бустом как то не сложилось (без него обходился, так как в основном Qt)…
Подробнее описание и обсуждение новых фич (там правда начиналось обсуждение, когда еще не выкинули концепты) можно посмотреть на этом форуме: forum.sources.ru/index.php?showtopic=190375
>Лямбда выражение может получать доступ практически к любым переменным и использовать их внутри тела.

Непонятная формулировка. Что значит к практически любым? Может к переменным из текущей области видимости?
Имеются виду переменные текущего блока кода.
Лямбда живет только в теле функции или её можно возвращать как результат и принимать как параметр?
Я лично не проверял, но на сайте у них написано " экспериментально "
понятно, значит может быть в 4.5 включат, а может и не включат. Попробую дома на VS2010
Мне вот что интересно, какие еще применения могут быть у лямбды кроме как обертка к глобальной переменной? Уж сильно выбивает предполагаемый синтаксис.
видимо вы не знакомы или очень мало пользуетесь boost и stl
boost практически не пользуюсь. А в случае stl, уж не знаю интерфейсы-то устоялись, в основном. Или вы о чем-то другом?
в статье отличный пример с использованием стандартного алгоритма, а уж всякие

std::for_each
std::accumulate
std::copy
std::transform

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

for( i = 0; i<container.size(); ++i){
//somecode
};
ладно с for_each. То что можно в sort или stable_partition на месте написать что надо делать вот это будет замечательно.
а что, вы stl контейнеры только сортируете и ничего больше с ними не делаете? :)
Понятно что не только сортирую. Просто внятной заменой for_each и компании является обычный for. Что в конкретном случае удобнее зависит от многих факторов, но замена одного на другое обычно не принесет такой кучи проблем как написание своей реализации qsort.
оригинально конечно, вы что предпочитаете писать каждый раз

for( iter=container.begin();iter!=container.end(); ++iter){
//some code on *iter
};

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

end_iter=container.end()

потом вспоминать, что *iter тоже функция и лучше бы ее тоже отдельно взять:
elem &e= *iter;

не проще ли сразу:

std::for_each( container.begin(), container.end(), [&](){//some code here} );

? :)
Хм компилятор который не заинлайнит container.end() в данном случае это что-то страшное =).
И да я рад возможности писать for_each с лямбдами, но опять же есть достаточно простые пути без него обойтись (даже на странном компиляторе =).
а какая разница заинлайнин он или нет? это какаято работа, .end() у нас в общем случае это не одна строка кода, смотрим к примеру для вектора:

end()
{ return iterator(this->_M_impl._M_finish); }

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

а если посмотреть в std::string::end() или еще какой-нибудь сложный контейнер?

В общем случае заинлайнить container.end() невозможно, так что надо на практических примерах смотреть или писать, как упомянули выше.
хммм… в принципе, картина прояснилась. Спасибо.
Мдаа, на редкость долбанутый синтаксис, круче только Перл — он абсолютно невменяем.
синтаксис в угоду компактности, для которой собственно лямбда и предназначена
В коментариях много пишут про корявый синтаксис. Как по мне это достаточно удачная попытка вставить замыкания в язык без сборщика мусора. Компилятор мало знает про планы программиста о жизни переменных и о том считает ли он их «тяжелыми» или нет. Поэтому надо же как то указать что передается по значению а что по ссылке.

Насчет полезности: кроме очевидных примеров с stl функциями которые требуют функтор можно найти еще кучу применений. Например кода есть куски кода которые очень похожи, но если их вынести в отдельную функцию то надо передавать 28 переменных. В таком случае можно объявить лямбда функцию и вызывать ее прямо на месте.
Те же qt сигнали реализованные на tr1::function + лямбда функции были бы гораздо удобнее.

Вобщем по-моему замечательное дополнение к языку
как это мало знает? :)

лямбда функция живет в блоке кода :) ей доступны переменные «на стеке» из этого блока, по выходу из блока все прекрасно «со стека» уберется :)

лямбда функция переданная как параметр в другой блок — не имеет доступа к переменным текущего блока :) поэтому никаких проблем без сборщика мусора нет.

Вообще в старом с++ всё тоже самое можно было бы сделать «ручками» написав чтото типа

class MyLambda{
sometype &someval;
sometype &someval;

public:
MyLambda( &some, &some… );
void operator()( params )
};

но каждый раз это делать несколько задалбывает
void f() {
int* ptr = new int(0);
return [=](){return (*ptr)++;};
}

по мне так все нормально с доступом к перемнной на стеке в текущем блоке и передавать ее надо по значению. В другом месте надо по ссылке передать. Так что не все понятно.
вы всегда так бесстрашно с сырыми указателями в с++ работаете? :)

я еще мощнее вам могу пример рассказать из с++

int *f(){
int val = 0;
return &val;
};

тут уже выше неоднократно высказывалось, что если инструмент позволяет гвоздь и забить, и закрутить и закатать внутрь стены, а мастер знает только то что инструмент для закатывания, то мастер сам себе дурак :)
Что-то у нас не выходит осмысленного обсуждения. Я привожу пример который хочется записать одной строчкой а вы мне объясняете что я ничего не понимаю и с указателями так работать нельзя. То ли у меня не получается выражать свои мысли то ли у вас их понимать а может и то и другое. Я думаю стоит свернуть дискуссию и признать что вы во всем правы =).
наверное ваш пример должен выглядеть так:

int f(){
int *ptr = new int(0);
return [=](){ return (*ptr)++ };
};

да вы тут потеряете память, но причем тут лямбда функции?

точно также можно написать без лямбды:

int f(){
int *ptr = new int(0);
return (*ptr)++;
};
Прошу прощения на самом деле я хотел написать

tr1::function<int(void)> f() {....}

Если вас волнует потеря памяти (я понимаю при работе с c++ это уже инстинкт =) то можно написать что-то вроде.
typedef tr1::function<int(void)> F;
pair<F, F> f() {
shared_ptrptr(new int(0));
return make_pair([=ptr](){ return (*ptr)++;}, [=ptr](){ return (*ptr)--;});
}
Понятно что такое решение ни полраза не потокобезопасно. Но с другой стороны мы получили пару функций связанных одной переменной без необходимости хранить эту переменную где-то еще.

У нас наблюдается терменологическая путаница. Есть лямбда функции. Они в данном контексте не более чем функции без имени. Так сказать «syntactic sugar». Но часто вместе с лямбда функциями используются замыкания. Замыкания как я представляю нужны для того чтобы сохранить часть переменных для последующего использования внутри лямбда функции.

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

например
void f(vector* v) {
hash_setbig_hash_set;
// fill big_hash_set
stable_partition(v->begin(), v->end(), [&big_hash_set](int i) { return big_hash_set.count(i); }

}

Если бы мне пришлось разрабатывать замыкания в c++, то я бы, скорее всего, остановился на том, чтобы все хранить по значению. Те кого это не устраивает могли бы передавать все через указатели (умные и не очень). В таком случае мой пример выглядел бы как

void f(vector* v) {
hash_setbig_hash_set;
// fill big_hash_set
hash_set* big_hash_set_ptr = &big_hash_set;
stable_partition(v->begin(), v->end(), (int i) { return big_hash_set_ptr->count(i); }

}

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

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

если по ссылке, то отражается.

Насчет замыканий. Было бы странно ожидать работоспособности такого кода:

boost::function<void()> func;

void f(){
int a = 3;
func = [&](){ a++; };
};

f();
func();

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

P.S. что собственно и описано в пропозале:

9… If one or more names in the effective capture set are preceded by &, the effect of invoking a closure object, or a copy, after the innermost block scope of the context of the lambda expression has been exited is undefined.
«никак не отражаются на лямбде» читать как «никак не отражаются на перменных вне оямбды»
интересно наблюдать, как с каждым витком развития в с++ пытаются запилить всё больше реально удобных фич lisp'a (буквально наднях читал про «генератор кода», т.н. t4 — по сути: попытка повторить макросы cl)
вы путаете, t4 это для C#
возможно, на истину в последней инстанции не претендую (относительно с++ и с# интерес сугубо созерцательный).
T4 — это ни черта не макросы lisp. t4 — просто текстовый шаблонизатор (в прямом смысле этого слова)/formatter/replacer, не более. Про семантику языка, AST и прочее он не знает.
вот бы еще поддержку делегатов без извращений с шаблонами…

Один из вариантов заполнения массива, используя текущий стандарт, приведен ниже.

[Пусто]

Теперь реализуем туже самую задачу с помощью функционального объекта. Вариант реализации приведен ниже.

[Пусто]

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

[Пусто]

Информация со временем исчезает из Хабара? Там были картинки или текст?

Sign up to leave a comment.

Articles