Какова мотивация лямбда-выражений в C ++ 11? - PullRequest
20 голосов
/ 11 июня 2010

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

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

Ответы [ 8 ]

43 голосов
/ 11 июня 2010

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

C ++ позволяет в определенной степени Функциональное программирование .Учтите это:

std::for_each( begin, end, doer );

Проблема в том, что функция (объект) doer

  1. указывает , что сделано в цикле
  2. но в некоторой степени скрывает то, что на самом деле сделано (вам нужно посмотреть реализацию operator() объекта функции)
  3. должно быть определено в другой области действия чем std::for_each вызов
  4. содержит определенное количество шаблонный код
  5. часто одноразовый код , который не используется ни для чего, кроме этогоконструкция с одним циклом

Лямбды значительно улучшают все это (и, может быть, еще немного я забыл).

41 голосов
/ 11 июня 2010

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

13 голосов
/ 11 июня 2010

Само по себе никакого выигрыша в производительности нет, но необходимость в лямбде возникла как следствие широкого принятия STL и его дизайнерских идей.

В частности, алгоритмы STL часто используют функторы. Без лямбды эти функторы должны быть предварительно объявлены для использования. Лямбды позволяют иметь «анонимные» функторы на месте.

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

Я, например, часто использую STL, но без C ++ 0x я использую гораздо больше циклов for (), чем алгоритм for_each () и его двоюродные братья. Это потому, что если бы я вместо этого использовал for_each (), мне нужно было бы получить код внутри цикла и объявить для него функтор. Также все локальные переменные перед циклом были бы недоступны, поэтому мне нужно было бы написать дополнительный код, чтобы передать их в качестве параметров конструктору функтора, или другой эквивалент. Как следствие, я склонен не использовать for_each (), если нет сильной мотивации, иначе код будет длиннее и сложнее для чтения.

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

9 голосов
/ 11 июня 2010

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

std::for_each(begin, end, unknown_function);

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

8 голосов
/ 11 июня 2010

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

Можно утверждать, что это способствует удобочитаемости кода (если ваш функтор объявлен как inline, где он используется).

5 голосов
/ 11 июня 2010

Хотя я думаю, что другие части C ++ 0x более важны , лямбда-выражения - это больше, чем просто "синтаксический сахар" для функциональных объектов в стиле C ++ 98, поскольку они могут захватывать контексты и поэтому по имени и затем они могут взять эти контексты в другом месте и выполнить. Это что-то новое, а не то, что «компилируется быстрее / медленнее».

#include <iostream>
#include <vector>
#include <functional>
void something_else(std::function<void()> f)
{
        f(); // A closure! I wonder if we can write in CPS now...
}
int main()
{
        std::vector<int> v(10,0);
        std::function<void ()> f = [&](){ std::cout << v.size() << std::endl; };

        something_else(f);
}
2 голосов
/ 11 июня 2010

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

Чаще всего программист выполняет итерации по диапазону элементов (поиск элемента, накопление элементов, сортировка элементов и т. Д.).Используя функциональный стиль, вы сразу видите, что программист намеревается сделать, в отличие от использования для циклов, где все «выглядит» одинаково.

Сравните алгоритмы + лямбда:

iterator longest_tree = std::max_element(forest.begin(), forest.end(), [height]{arg0.height>arg1.height});
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), []{is_leaf(arg0)});
std::transform(forest.begin(), forest.end(), firewood.begin(), []{arg0.trans(...));
std::for_each(forest.begin(), forest.end(), {arg0.make_plywood()});

сстарые циклы forschool;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it.height() > *longest_tree.height()) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

(я знаю, что этот фрагмент кода содержит синтаксические ошибки.)

2 голосов
/ 11 июня 2010

Ну, сравните это:

int main () {
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19};

    int center = 10;
    std::sort(x.begin(), x.end(), [=](int x, int y) {
        return abs(x - center) < abs(y - center);
    });

    std::for_each(x.begin(), x.end(), [](int v) {
        printf("%d\n", v);
    });

    return 0;
}

с этим:

// why enforce this to be defined nonlocally?
void printer(int v) {
    printf("%d\n", v);
} 

int main () {
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19};

    // why enforce we to define a whole struct just need to maintain a state?
    struct {
        int center; 
        bool operator()(int x, int y) const {
            return abs(x - center) < abs(y - center);
        }
    } comp = {10};

    std::sort(x.begin(), x.end(), comp);

    std::for_each(x.begin(), x.end(), printer);

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...