Каковы различия между этими 4 лямбда-выражениями? - PullRequest
0 голосов
/ 25 октября 2018

Я знаю, что это может показаться очень глупым для не-noob C++ разработчиков, но каковы различия между этими 4 лямбда-выражениями? Код :

#include <iostream>
#include <math.h>
#include <functional>

inline double MyFunction(double a, double b, double c) {
    return (a + b + c);
}

inline void FunctionWrapper(std::function<double(double)> tempFunct, double value) {
    std::function<double(double)> funct;

    funct = tempFunct;

    std::cout << "result: " << funct(value) << std::endl;
}

int main()
{    
    double value = 100.0;

    FunctionWrapper([](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);

    FunctionWrapper([value](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([value](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);
}

Кажется, он делает то же самое?Или с использованием двух разных «нотаций» и использованием значения в качестве замыкания?

Ответы [ 4 ]

0 голосов
/ 25 октября 2018

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

Разница между вашими первыми двумя лямбдами и лямбдами три и четыре - захват.Но ваши примеры не подходят для иллюстрации эффектов захвата.Посмотрите на следующий код ( live demo ).

int main()
{    
    double v = 100.0;

    auto lam1= [](double val) { return MyFunction(val, 1.0, 2.0); }; //A
    auto lam2= [v](double val) { return MyFunction(v, 1.0, 2.0); }; //B
    auto lam3= [&v](double val) { return MyFunction(v, 1.0, 2.0); }; //C

    FunctionWrapper( lam1, v++ ); //1
    FunctionWrapper( lam2, v++ ); //2
    FunctionWrapper( lam3, v++ ); //3
    std::cout << "v: " << v << '\n';
}

Хотя lam1 ничего не захватывает, lam2 захватывает v по значению и lam3 захватывает v по ссылке.Выход трех вызовов FunctionWrapper составляет 103, 103, 106, соответственно.Это потому, что хотя v было изменено в строке //1, в строке //2 используется старое значение.То есть захват по значению означает, что значение v сохраняется в то время, когда lam2 инициализируется в строке //B, сохраняется в lam2.С другой стороны, в строке //3 используется текущее значение v.Это потому, что lam3 содержит ссылку на v.

0 голосов
/ 25 октября 2018

В этом контексте все они дают одинаковые результаты.Однако между ними есть логические различия.

  • [](double value) { return MyFunction(value, 1.0, 2.0); }

    Это лямбда, которая принимает один параметр типа value и передает его в MyFunction.Его тип возвращаемого значения выводится из оператора return для типа MyFunction, который равен double.

  • [](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    Это та же самая лямбдакак и раньше, но на этот раз явно указывается тип возвращаемого значения double.Это то же самое в этом случае, но оно будет отличаться от первого, если тип возвращаемого значения MyFunction был бы другим.В этом случае первый вернет то, что вернет MyFunction, в то время как этот все еще вернет double.

  • [value](double value) { return MyFunction(value, 1.0, 2.0); }

    Этот зависит отстандартная версия используется.В C ++ 11 и 14 этот захватывает локальную переменную main value.Однако этот захват скрыт лямбда-параметром value, поэтому он практически бесполезен.Было бы иначе, если бы лямбда была объявлена ​​как, например, [value](double v) { return MyFunction(value, 1.0, 2.0); }.Это передало бы захваченный value, а не его параметр.

    В C ++ 17 и выше, это было изменено, и это на самом деле неправильно (ошибка компиляции).Называть лямбда-параметр так же, как и то, что вы записываете, больше не разрешается.

    Поскольку изменение было отчетом о дефекте ( CWG 2211 ), оно применяется задним числом и поэтому компиляторы могут отклонитьтакой код даже в более ранних версиях C ++.

  • [value](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    То же, что лямбда-номер 3, с явным указанием типа возвращаемого значения (разница между 3 и 4 в точности равнато же, что между 1 и 2).

0 голосов
/ 25 октября 2018

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

Третий и четвертый лямбды не сформированы, поскольку они объявляют параметр с тем же именем в качестве перехвата.См. Стандартное правило:

[expr.prim.lambda.capture]

Если идентификатор в простом захвате отображается как идентификатор объявления параметра лямбда-выраженияПараметр-объявление-объявление-декларатор, программа некорректна.[Пример:

void f() {
  int x = 0;
  auto g = [x](int x) { return 0; }    // error: parameter and simple-capture have the same name
}

- конец примера]

Эта формулировка была принята в C ++ 17.

0 голосов
/ 25 октября 2018

Первая лямбда - обычная.

Вторая лямбда указывает, что тип возвращаемого значения - double.

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

Полагаю, они должны выглядеть примерно так:

[foo](double value) { return MyFunction(value, 1.0, foo); }, value);
...