เหตุผลใดที่จะไม่ใช้ lambdas ทั่วโลก?


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
}

การเปลี่ยนไปใช้ฟังก์ชั่นที่เหมาะสมนั้นเป็นเรื่องเล็กน้อย แต่มันทำให้ฉันสงสัยว่ามีเหตุผลบางอย่างที่จะไม่ปล่อยให้มันเป็นแลมบ์ดาบ้างไหม? มีเหตุผลใดที่ไม่เพียงแค่ใช้ lambdas ทุกที่แทนที่จะใช้ฟังก์ชั่นสากล "ปกติ"?


ฉันถือว่าการจับตัวแปรที่ไม่ได้ตั้งใจจะนำไปสู่ข้อผิดพลาดมากมายหาก lambdas ไม่ได้ถูกใช้อย่างระมัดระวัง
macroland

ข้อโต้แย้งบางประการสำหรับyoutube.com/watch?v=Ft3zFk7VPrE
sudo rm -rf slash

คำตอบ:


61

มีเหตุผลสำคัญอย่างหนึ่งที่จะไม่ใช้ lambdas ทั่วโลกเพราะไม่ปกติ

ไวยากรณ์ของฟังก์ชัน C ++ เป็นปกติมาตั้งแต่สมัยของ C โปรแกรมเมอร์ได้รู้จักมานานหลายทศวรรษว่าไวยากรณ์หมายถึงอะไรและวิธีการทำงานอย่างไร หากโปรแกรมเมอร์ C ++ ทุกระดับความสามารถเกินกว่า "utter newbie" เห็นคำจำกัดความของฟังก์ชันพวกเขารู้ว่าพวกเขากำลังได้อะไร

แลมบ์ดาทั่วโลกนั้นเป็นสัตว์ที่แตกต่างออกไปโดยสิ้นเชิง มันมีพฤติกรรมที่แตกต่างจากฟังก์ชั่นปกติ Lambdas เป็นวัตถุในขณะที่ฟังก์ชั่นไม่ใช่ พวกเขามีประเภท แต่ประเภทนั้นแตกต่างจากประเภทของฟังก์ชั่นของพวกเขา และอื่น ๆ

ดังนั้นตอนนี้คุณได้ยกระดับการสื่อสารกับโปรแกรมเมอร์คนอื่น ๆ โปรแกรมเมอร์ C ++ ต้องเข้าใจ lambdas หากพวกเขาจะเข้าใจว่าฟังก์ชันนี้ทำอะไรอยู่ และใช่นี่คือ 2019 ดังนั้นโปรแกรมเมอร์ C ++ ที่ดีควรมีความคิดว่าแลมบ์ดามีลักษณะอย่างไร แต่มันก็ยังคงเป็นบาร์ที่สูงกว่า

และถึงแม้ว่าพวกเขาจะเข้าใจคำถามในใจของโปรแกรมเมอร์นั้นก็คือ ... ทำไมผู้เขียนรหัสนี้ถึงเขียนอย่างนั้น? และถ้าคุณไม่มีคำตอบที่ดีสำหรับคำถามนั้น (ตัวอย่างเช่นเนื่องจากคุณต้องการห้ามการบรรทุกเกินพิกัดอย่างชัดเจนในจุดกำหนดเองของ Ranges) คุณควรใช้กลไกทั่วไป

ชอบวิธีแก้ไขปัญหาที่คาดว่าจะนวนิยายที่เหมาะสม ใช้วิธีการที่ซับซ้อนน้อยที่สุดเพื่อให้ได้คะแนนมา


9
"ตั้งแต่วันที่ C"วิธีก่อนหน้านั้นเพื่อนของฉัน
การแข่งขัน Lightness ใน Orbit

4
@LightnessRaces: ประเด็นหลักของฉันคือการแสดงว่าไวยากรณ์ของฟังก์ชัน C ++ มีมาตั้งแต่ C ดังนั้นผู้คนมากมายรู้ว่ามันคืออะไรโดยการตรวจสอบ
Nicol Bolas

2
เห็นด้วยกับคำตอบทั้งหมดยกเว้นประโยคแรก "มันไม่ปกติ" เป็นคำที่คลุมเครือและคลุมเครือ บางทีคุณอาจหมายถึงมันในแง่ของ "มันผิดปกติและสับสน"?
einpoklum

1
@einpoklum: C ++ มีหลายสิ่งหลายอย่างที่อาจถือได้ว่า "ผิดปกติและสับสน" ซึ่งยังคงเป็น "ปกติ" ภายในโดเมนของพวกเขา (ดูที่เทมเพลต metaprogramming แบบลึก) ฉันคิดว่า "ปกติ" ใช้ได้อย่างสมบูรณ์ในกรณีนี้ มันไม่ใช่สิ่งที่อยู่ใน "บรรทัดฐาน" ของประสบการณ์การเขียนโปรแกรม C ++ ทั้งในอดีตและปัจจุบัน
Nicol Bolas

@ NicolBolas: แต่ในแง่นั้นมันเป็นเหตุผลแบบวงกลม: OP สงสัยว่าเราควรจะนำการปฏิบัติที่หายากมาใช้หรือไม่ การปฏิบัติที่หายากไม่ได้ "บรรทัดฐาน" เกือบตามคำนิยาม แต่นาโนเมตร
einpoklum

53

ฉันสามารถนึกถึงเหตุผลบางอย่างที่คุณต้องการหลีกเลี่ยง lambdas ทั่วโลกเพื่อทดแทนดรอปอินสำหรับฟังก์ชั่นปกติ:

  • ฟังก์ชั่นปกติสามารถโอเวอร์โหลดได้; lambdas ไม่สามารถ (มีเทคนิคในการจำลองสิ่งนี้)
  • แม้ว่าพวกเขาจะมีฟังก์ชั่นเหมือนกัน แต่แลมบ์ดาที่ไม่ได้ถ่ายภาพแบบนี้จะครอบครองหน่วยความจำ (โดยทั่วไปคือ 1 ไบต์สำหรับการไม่ถ่ายภาพ)
    • ตามที่ระบุไว้ในความคิดเห็นคอมไพเลอร์สมัยใหม่จะปรับการจัดเก็บนี้ให้อยู่ภายใต้กฎราวกับ

"ทำไมฉันไม่ควรใช้แลมบ์ดาเพื่อแทนที่ฟังก์ชั่น stateful (คลาส)"

  • ชั้นเรียนมีข้อ จำกัด น้อยกว่า lambdas และดังนั้นจึงควรเป็นสิ่งแรกที่คุณเข้าถึง
    • (ข้อมูลสาธารณะ / ส่วนตัวการบรรทุกเกินพิกัดวิธีการช่วยเหลือ ฯลฯ )
  • หากแลมบ์ดามีสถานะแล้วมันเป็นเรื่องยากมากที่จะให้เหตุผลว่าเมื่อใดที่มันกลายเป็นโลก
    • เราควรสร้างตัวอย่างของคลาสที่ขอบเขตที่แคบที่สุดเท่าที่จะเป็นไปได้
  • เป็นการยากที่จะแปลงแลมบ์ดาที่ไม่ใช่การจับภาพไปเป็นตัวชี้ฟังก์ชั่นและเป็นไปไม่ได้สำหรับแลมบ์ดาที่ระบุสิ่งใด ๆ ในการดักจับ
    • คลาสทำให้เรามีวิธีที่ตรงไปตรงมาในการสร้างพอยน์เตอร์ของฟังก์ชั่นและพวกมันยังเป็นโปรแกรมเมอร์ที่สะดวกสบายกว่าด้วย
  • Lambdas ที่มีการดักจับใด ๆ ไม่สามารถสร้างเป็นค่าเริ่มต้นได้ (ใน C ++ 20 ก่อนหน้านี้ไม่มี Constructor เริ่มต้นในทุกกรณี)

นอกจากนี้ยังมีตัวชี้ "นี้" โดยนัยไปยังแลมบ์ดา (แม้จะไม่ได้ทำการตรวจจับ) ซึ่งส่งผลให้มีพารามิเตอร์พิเศษเมื่อเรียกแลมบ์ดากับการเรียกใช้ฟังก์ชัน
1201ProgramAlarm

@ 1201ProgramAlarm: นั่นเป็นจุดที่ดี; มันอาจเป็นเรื่องยากมากขึ้นที่จะได้รับตัวชี้ฟังก์ชั่นสำหรับแลมบ์ดา (แม้ว่าแลมบ์ดาที่ไม่มีการจับภาพสามารถสลายตัวเป็นพอยน์เตอร์ฟังก์ชั่นทั่วไป)
AndyG

3
ด้านพลิกถ้ามันถูกส่งผ่านที่ไหนสักแห่งแทนที่จะเรียกโดยตรงว่ามีแลมบ์ดาแทนที่จะเป็นฟังก์ชั่นส่งเสริมการทำอินไลน์ โดยธรรมชาติเราสามารถแพ็คฟังก์ชั่นตัวชี้ในstd::integral_constant...
Deduplicator

@Dupuplicator: " การมีแลมบ์ดาแทนที่จะเป็นฟังก์ชั่นที่ส่งเสริมการทำอินไลน์ " เพื่อให้ชัดเจนนี่จะใช้เฉพาะในกรณีที่ "ที่ไหนสักแห่ง" เป็นแม่แบบที่ใช้ฟังก์ชั่นที่เรียกว่าเป็น callable ประเภทมากกว่าฟังก์ชั่นชี้
Nicol Bolas

2
@AndyG คุณลืมกฎ as-if: โปรแกรมที่ไม่ขอขนาดแลมบ์ดา (เพราะ ... ทำไม?!) หรือสร้างตัวชี้ไปยังมันไม่จำเป็นต้อง (และโดยทั่วไปไม่ได้) จัดสรรพื้นที่สำหรับมัน
Konrad Rudolph

9

หลังจากถามฉันคิดว่ามีเหตุผลที่จะไม่ทำสิ่งนี้: เนื่องจากสิ่งเหล่านี้เป็นตัวแปรพวกเขามีแนวโน้มที่จะสั่งงานการเริ่มต้นแบบคงที่ Fiasco ( https://isocpp.org/wiki/faq/ctors#static-init-order ) ซึ่งสามารถ ทำให้เกิดข้อบกพร่องลงมา


คุณสามารถทำให้ลูกconstexprแกะ ... จุดที่แท้จริงคือ: แค่ใช้ฟังก์ชั่น
einpoklum

8

มีเหตุผลใดที่ไม่เพียงแค่ใช้ lambdas ทุกที่แทนที่จะใช้ฟังก์ชั่นสากล "ปกติ"?

ปัญหาของความซับซ้อนในระดับหนึ่งต้องใช้วิธีแก้ปัญหาอย่างน้อยก็ความซับซ้อนเดียวกัน แต่ถ้ามีวิธีแก้ปัญหาที่ซับซ้อนน้อยกว่าสำหรับปัญหาเดียวกันแสดงว่าไม่มีเหตุผลอันสมควรที่จะใช้วิธีที่ซับซ้อนกว่านี้ ทำไมต้องแนะนำความซับซ้อนที่คุณไม่ต้องการ

ระหว่างแลมบ์ดากับฟังก์ชั่นฟังก์ชั่นนั้นเป็นเอนทิตีที่ซับซ้อนน้อยกว่าของทั้งสอง คุณไม่จำเป็นต้องพิสูจน์ว่าไม่ได้ใช้แลมบ์ดา คุณต้องแสดงให้เห็นถึงการใช้ การแสดงออกแลมบ์ดาแนะนำประเภทการปิดซึ่งเป็นประเภทคลาสที่ไม่มีชื่อพร้อมกับฟังก์ชั่นพิเศษสมาชิกปกติผู้ประกอบการเรียกใช้ฟังก์ชั่นและในกรณีนี้ผู้ประกอบการแปลงโดยนัยไปยังตัวชี้ฟังก์ชั่นและสร้างวัตถุประเภทนั้น การเริ่มต้นคัดลอกตัวแปรโกลบอลจากนิพจน์แลมบ์ดานั้นทำอะไรได้มากกว่าการกำหนดฟังก์ชั่น มันกำหนดประเภทชั้นเรียนที่มีหกฟังก์ชั่นประกาศโดยปริยายกำหนดสองฟังก์ชั่นการดำเนินการเพิ่มเติมและสร้างวัตถุ คอมไพเลอร์ต้องทำมากขึ้น หากคุณไม่ต้องการคุณสมบัติใด ๆ ของแลมบ์ดาอย่าใช้แลมบ์ดา ...


6

lambdas มีฟังก์ชั่นที่ไม่ระบุชื่อ

หากคุณใช้แลมบ์ดาที่มีชื่อหมายความว่าคุณใช้ฟังก์ชันนิรนามที่มีชื่อ เพื่อหลีกเลี่ยง oxymoron นี้คุณอาจใช้ฟังก์ชันเช่นกัน


1
สิ่งนี้ดูเหมือนจะเป็นข้อโต้แย้งจากคำศัพท์เช่น การตั้งชื่อและความหมายที่สับสน มันสามารถหักล้างได้ง่ายโดยการกำหนดว่าแลมบ์ดาที่มีชื่อนั้นจะไม่ระบุชื่ออีกต่อไป (duh) ปัญหาที่นี่ไม่ใช่วิธีการใช้แลมบ์ดานั่นคือ "ฟังก์ชั่นที่ไม่ระบุชื่อ" เป็นผู้เรียกชื่อผิดและไม่แม่นยำอีกต่อไปเมื่อแลมบ์ดาผูกพันชื่อ
Konrad Rudolph

@ KonradRudolph: มันไม่ได้เป็นเพียงคำศัพท์นั่นเป็นเหตุผลที่พวกเขาถูกสร้างขึ้นในสถานที่แรก อย่างน้อยในอดีตแลมบ์ดาเป็นฟังก์ชั่นนิรนามและนั่นเป็นสาเหตุที่ทำให้เกิดความสับสนในการตั้งชื่อพวกเขาโดยเฉพาะอย่างยิ่งเมื่อคาดว่าจะมีฟังก์ชั่นแทน
Eric Duminil

1
Nah แลมบ์ดามีการตั้งชื่อเป็นประจำ (โดยปกติจะอยู่ในพื้นที่) ไม่มีอะไรสับสนเกี่ยวกับเรื่องนั้น
Konrad Rudolph

1
ไม่เห็นด้วยกับฟังก์ชั่นที่ไม่ระบุชื่อมันเป็น functor มากกว่า แต่อย่างไรก็ตามฉันไม่เห็นปัญหาที่มีอินสแตนซ์ (ตั้งชื่อ) แทนการบังคับให้ใช้พวกเขาเป็นการอ้างอิง rvalue เท่านั้น (ตามที่คุณควรจะใช้ในขอบเขตท้องถิ่น)
Jarod42

5

หากมีเหตุผลบางอย่างที่จะไม่ปล่อยให้มันเป็นแลมบ์ดา? มีเหตุผลใดที่ไม่เพียงแค่ใช้ lambdas ทุกที่แทนที่จะใช้ฟังก์ชั่นสากล "ปกติ"?

เราเคยใช้ฟังก์ชั่นแทน functor ระดับโลกดังนั้นมันจึงแบ่งความเชื่อมโยงและหลักการของความประหลาดใจน้อยที่สุด

ความแตกต่างที่สำคัญคือ:

  • ฟังก์ชันสามารถโอเวอร์โหลดได้ในขณะที่ฟังก์ชั่นไม่สามารถทำได้
  • ฟังก์ชั่นสามารถพบได้กับ ADL ไม่ใช่ฟังก์ชั่น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.