Якась причина не використовувати глобальні лямбда?


89

У нас була функція, яка використовувала внутрішню для себе лямбда, яка не захоплює, наприклад:

void foo() {
  auto bar = [](int a, int b){ return a + b; }

  // code using bar(x,y) a bunch of times
}

Тепер функціонал, реалізований лямбда, став необхідним в іншому місці, тому я збираюся підняти лямбда з foo()області глобального / простору імен. Я можу залишити його як лямбда, зробивши його опцією копіювання-вставлення, або змінити його на належну функцію:

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2

void foo() {
  // code using bar(x,y) a bunch of times
}

Зміна його на належну функцію є тривіальною, але це змусило мене задуматися, чи є якась причина, щоб не залишити її як лямбда? Чи є якась причина не просто використовувати лямбди всюди замість «регулярних» глобальних функцій?


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

деякі аргументи для цього youtube.com/watch?v=Ft3zFk7VPrE
sudo rm -rf slash

Відповіді:


61

Є одна дуже важлива причина не використовувати глобальні лямбда: адже це не нормально.

Синтаксис регулярних функцій C ++ існує ще з часів С. Програмісти десятиліттями знали, що означає зазначений синтаксис і як вони працюють (хоча, правда, вся річ розпаду функції на вказівник іноді кусає навіть досвідчених програмістів). Якщо програміст на C ++ будь-якого рівня навичок, що перевищує "зовсім новачків", бачить визначення функції, вони знають, що вони отримують.

Глобальна лямбда - це зовсім інший звір. Він відрізняється поведінкою від звичайної функції. Лямбди - це об'єкти, а функції - ні. Вони мають тип, але цей тип відрізняється від типу їх функції. І так далі.

Тож тепер ви підняли планку у спілкуванні з іншими програмістами. Програміст C ++ повинен розуміти лямбда, якщо вони збираються зрозуміти, що ця функція виконує. І так, це 2019 рік, тому гідний програміст C ++ повинен мати уявлення про те, як виглядає лямбда. Але це все ж вища планка.

І навіть якщо вони це зрозуміють, питання про те, що розуміє програміст, буде ... чому автор цього коду написав саме так? І якщо ви не маєте належної відповіді на це питання (наприклад, тому, що ви явно хочете заборонити перевантаження, як у пунктах налаштування діапазонів), тоді ви повинні використовувати загальний механізм.

Віддайте перевагу очікуваним рішенням нових варіантів, де це доречно. Скористайтеся найменш складним методом добору точки.


9
"З часів С" Шлях до того мого друга
Гонки

4
@LightnessRaces: Моя основна суть полягала в тому, щоб висловити, що синтаксис функцій C ++ існує вже з C, тому багато людей знають, що це за інспекція.
Нікол

2
Погодьтеся з усією цією відповіддю, крім першого речення. «Це не нормально» - це розпливчасте і неясне твердження. Можливо, ви мали на увазі це в сенсі "це нечасто і заплутано"?
einpoklum

1
@einpoklum: У C ++ є багато речей, які можна вважати "незвичайними та заплутаними", які все ще є цілком "нормальними" у своїй області (див. метапрограмування глибокого шаблону). Я думаю, що "нормальне" в цьому випадку цілком справедливо; це не річ, яка входить в "норми" досвіду програмування C ++, як історично, так і сьогодні.
Нікол

@NicolBolas: Але в цьому сенсі це кругові міркування: ОП цікавиться, чи варто нам застосовувати рідкісну практику. Рідкісна практика не є «нормою», майже за визначенням. Але нм.
einpoklum

53

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

  • регулярні функції можуть бути перевантажені; лямбдас не може (проте є методи, що імітують це)
  • Незважаючи на те, що вони подібні до функцій, навіть лямбда, що не схоплює, буде займати пам'ять (як правило, 1 байт для незахоплення).
    • як було зазначено в коментарях, сучасні компілятори оптимізують це сховище далеко за правилом ніби

"Чому я не повинен використовувати лямбда для заміни видатних функторів (класів)?"

  • заняття просто мають менше обмежень, ніж лямбда, і тому слід бути першим, до чого ви прагнете
    • (публічні / приватні дані, перевантаження, допоміжні методи тощо)
  • якщо лямбда має стан, то міркувати про те, коли вона стає глобальною, все складніше.
    • Ми мусимо вважати за краще створити екземпляр класу в найкоротшій мірі
  • перетворити лямбда, що не захоплює, вже важко перетворити на функціональний вказівник, а лямбда, яка вказує що-небудь при її захопленні, неможливо.
    • класи дають нам простий спосіб створення покажчиків функцій, і вони також є тим, що багатьом програмістам зручніше
  • Лямбди з будь-яким захопленням не можуть бути побудовані за замовчуванням (у C ++ 20. Раніше не було конструктора за замовчуванням у будь-якому випадку)

Існує також неявний "цей" покажчик на лямбда (навіть той, що не захоплює), що призводить до отримання додаткового параметра при виклику лямбда-проти виклику функції.
1201ProgramAlarm

@ 1201ProgramAlarm: Це хороший момент; отримати вказівник функції лямбда може бути набагато складніше (хоча лямбди, що не захоплюють, можуть розкластись на звичайні покажчики функцій)
AndyG

3
На зворотному боці, якщо він передається кудись, а не безпосередньо викликається, наявність лямбда замість функції сприяє вставці. Природно, можна впакувати функцію-покажчик у astd::integral_constant протягом цього ...
Deduplicator

@Deduplicator: " наявність лямбда замість функції сприяє вкладенню " Просто для того, щоб бути зрозумілим, це застосовується лише в тому випадку, якщо "десь" є шаблон, який приймає функцію, яку вона викликає як довільний тип дзвінка, а не покажчик функції.
Нікол

2
@AndyG Ви забуваєте правило як би: програми, які не вимагають розміру лямбда (тому що ... чому ?!) і не створюють покажчик на нього, не потрібно (і взагалі не потрібно) виділіть для нього простір.
Конрад Рудольф

9

Поцікавившись, я подумав про причину цього не робити: оскільки це змінні, вони схильні до порядку фіаско для статичної ініціалізації ( https://isocpp.org/wiki/faq/ctors#static-init-order ), який міг би викликати помилки вниз по лінії.


Ви можете зробити з них constexprлямбда ... справжній момент: Просто використовуйте функцію.
einpoklum

8

Чи є якась причина не просто використовувати лямбди всюди замість «регулярних» глобальних функцій?

Проблема певного рівня складності вимагає рішення хоча б тієї ж складності. Але якщо для однієї і тієї ж проблеми є менш складне рішення, то для використання більш складної не існує обґрунтування. Навіщо вводити складність, яка вам не потрібна?

Між лямбда-функцією функція - це просто менш складний вид сутності двох. Вам не доведеться виправдовуватися, що не використовуєте лямбда. Ви повинні виправдати його. Лямбда-вираз вводить тип закриття, який представляє собою неназваний тип класу зі всіма звичайними функціями спеціального члена, оператором виклику функції і, в цьому випадку, оператором неявного перетворення в функцію вказівника, і створює об'єкт цього типу. Копіювання ініціалізації глобальної змінної з лямбда-виразу просто робить набагато більше, ніж просто визначення функції. Він визначає тип класу з шістьма неявно оголошеними функціями, визначає ще дві функції оператора та створює об'єкт. Компілятор повинен зробити набагато більше. Якщо вам не потрібна жодна з функцій лямбда, тоді не використовуйте лямбда ...


6

Лямбди - це анонімні функції .

Якщо ви використовуєте іменовану лямбда, це означає, що ви в основному використовуєте іменовану анонімну функцію. Щоб уникнути цього оксиморону, ви також можете скористатися функцією.


1
Це, здається, є аргументом з термінології, тобто. плутані імена та значення. Це можна легко спростувати, визначивши, що названа лямбда більше не є анонімною (да). Проблема тут полягає не в тому, як використовується лямбда, це те, що "анонімна функція" є неправильним і не є точною, коли лямбда прив'язується до імені.
Конрад Рудольф

@KonradRudolph: Це не просто термінологія, тому вони створені в першу чергу. Принаймні, історично, лямбди - це анонімні функції, і тому їх називати незрозуміло, особливо, коли замість цього очікуватимуть функції.
Ерік Думініл

1
Ні. Лямбди називаються звичайно (як правило, локально), в цьому немає нічого заплутаного.
Конрад Рудольф

1
Не погоджуючись з анонімними функціями , це скоріше функтор, але все одно, я не бачу проблеми мати (названий) екземпляр замість того, щоб змусити використовувати їх лише як rvalue-посилання. (оскільки ваш пункт також повинен застосовуватися на місцевому рівні).
Jarod42

5

якщо є якась причина, щоб не залишити його як лямбда? Чи є якась причина не просто використовувати лямбди всюди замість «регулярних» глобальних функцій?

Ми використовували функції замість глобального функтора, тому це порушує когерентність та Принцип найменшого здивування .

Основні відмінності:

  • функції можуть бути перевантажені, тоді як функтори не можуть.
  • функції можна знайти в ADL, а не у функторів.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.