แลมบ์ดาจับและพารามิเตอร์ที่มีชื่อเดียวกันใครเป็นเงาของอีกฝ่าย? (เสียงดังกับ gcc)


125
auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • เสียงดัง ++ 3.6.0และใหม่กว่าพิมพ์"คุณกำลังใช้เสียงดัง ++!" และเตือนเกี่ยวกับการจับภาพ fooที่ไม่ได้ใช้งาน

  • g ++ 4.9.0และใหม่กว่าพิมพ์"คุณกำลังใช้ g ++!" และเตือนเกี่ยวกับพารามิเตอร์ fooที่ไม่ได้ใช้งาน

คอมไพเลอร์ใดที่ทำตามมาตรฐาน C ++ ได้แม่นยำกว่าที่นี่

ตัวอย่าง Wandbox


1
วางรหัสจาก wandbox ไปที่นี่ (?) (พวกเขาดูเหมือนจะลืมปุ่มแชร์) ทำให้มันดูเหมือน VS2015 เห็นด้วยกับเสียงดังกราวพูดเตือน C4458: ประกาศของสมาชิกคลาส
nwp

12
ตัวอย่างที่ดี ..
deviantfan

4
แลมบ์ดามีประเภทที่มีตัวดำเนินการเรียกฟังก์ชันแม่แบบดังนั้นตรรกะจะทำให้ฉันบอกว่าพารามิเตอร์ควรเงาตัวแปรที่จับได้ราวกับว่าในstruct Lambda { template<typename T> void operator()(T foo) const { /* ... */ } private: decltype(outer_foo) foo{outer_foo}; }.
skypjack

2
@nwp VS ผิดสมาชิกข้อมูลของแลมบ์ดาไม่มีชื่อจึงไม่สามารถเงาได้ มาตรฐานระบุว่า "การเข้าถึงเอนทิตีที่ถูกจับได้ถูกเปลี่ยนเป็นการเข้าถึงสมาชิกข้อมูลที่เกี่ยวข้อง" ซึ่งทำให้เราอยู่ที่ช่องสี่เหลี่ยม
. 'สรรพนาม' ม.

10
ฉันหวังว่าเวอร์ชันเสียงดังกล่าวจะถูกต้อง - มันจะเป็นการทำลายรากฐานใหม่หากสิ่งที่อยู่นอกฟังก์ชันทำให้พารามิเตอร์ของฟังก์ชันเป็นเงาแทนที่จะเป็นวิธีอื่น!
MM

คำตอบ:


65

อัปเดต: ตามที่สัญญาไว้โดยเก้าอี้หลักในใบเสนอราคาด้านล่างตอนนี้รหัสมีรูปแบบไม่ถูกต้อง :

หากมีการระบุในที่ง่ายต่อการจับภาพปรากฏเป็นdeclarator-IDของพารามิเตอร์ที่แลมบ์ดา-declarator 's พารามิเตอร์ประกาศข้อโปรแกรมจะไม่ดีเกิดขึ้น


มีปัญหาเล็กน้อยเกี่ยวกับการค้นหาชื่อใน lambdas เมื่อไม่นานมานี้ พวกเขาได้รับการแก้ไขโดยN2927 :

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

การค้นหาจะทำในบริบทของแลมบ์ดา - นิพจน์เสมอไม่ "หลังจาก" การเปลี่ยนแปลงไปยังเนื้อหาฟังก์ชันของสมาชิกประเภทการปิด ดู[expr.prim.lambda] / 8 :

แลมบ์ดาแสดงออกของสารประกอบงบผลผลิตฟังก์ชั่นของร่างกาย ([dcl.fct.def]) ของผู้ประกอบการฟังก์ชั่นการโทร แต่สำหรับวัตถุประสงค์ของการค้นหาชื่อ [ ... ] ที่ผสมคำสั่งมีการพิจารณาในบริบทของแลมบ์ดาแสดงออก [ ตัวอย่าง :

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};

- ตัวอย่างตอนท้าย ]

(ตัวอย่างยังทำให้ชัดเจนว่าการค้นหาไม่ได้พิจารณาสมาชิกการดักจับที่สร้างขึ้นของประเภทการปิด)

ชื่อfooไม่ได้ (อีกครั้ง) ประกาศในการจับภาพ; มันถูกประกาศในบล็อกที่มีนิพจน์แลมบ์ดา พารามิเตอร์fooถูกประกาศในบล็อกที่ซ้อนอยู่ในบล็อกด้านนอกนั้น (ดู[basic.scope.block] / 2ซึ่งกล่าวถึงพารามิเตอร์แลมบ์ดาอย่างชัดเจนด้วย) ลำดับของการค้นหาเป็นอย่างชัดเจนจากภายในบล็อกนอก ดังนั้นควรเลือกพารามิเตอร์นั่นคือเสียงดังถูกต้อง

หากคุณทำการจับภาพแบบเริ่มต้นเช่นfoo = ""แทนที่จะfooเป็นคำตอบจะไม่ชัดเจน เนื่องจากการจับในขณะนี้ทำให้เกิดการประกาศที่ไม่ได้รับ "บล็อก" ฉันส่งข้อความถึงเก้าอี้แกนกลางเรื่องนี้ใครตอบกลับ

นี่คือปัญหา 2211 (รายการปัญหาใหม่จะปรากฏบนไซต์ open-std.org ในไม่ช้าน่าเสียดายที่มีเพียงตัวยึดตำแหน่งสำหรับปัญหาหลายประการซึ่งเป็นปัญหาหนึ่งฉันกำลังทำงานอย่างเต็มที่เพื่อเติมเต็มช่องว่างเหล่านั้นก่อนที่ Kona ประชุมสิ้นเดือน) CWG พูดถึงเรื่องนี้ในระหว่างการประชุมทางไกลในเดือนมกราคมของเราและทิศทางคือทำให้โปรแกรมมีรูปแบบไม่ถูกต้องหากชื่อการจับภาพเป็นชื่อพารามิเตอร์ด้วย


ไม่มีอะไรสำหรับฉันที่จะแยกออกจากกันที่นี่ :) การจับภาพแบบธรรมดาไม่ได้ประกาศอะไรดังนั้นผลลัพธ์ที่ถูกต้องของการค้นหาชื่อจึงค่อนข้างชัดเจน (BTW, GCC ทำให้ถูกต้องหากคุณใช้ค่าเริ่มต้นในการจับภาพแทนการจับภาพอย่างชัดเจน) init-capture s ค่อนข้างยุ่งยากกว่า
TC

1
@TC ฉันเห็นด้วย ฉันยื่นประเด็นหลัก แต่เห็นได้ชัดว่ามีการพูดคุยกันแล้วโปรดดูคำตอบที่แก้ไข
Columbo

6

ฉันพยายามรวบรวมความคิดเห็นสองสามข้อของคำถามเพื่อให้คำตอบที่มีความหมายแก่คุณ
ก่อนอื่นโปรดทราบว่า:

  • สมาชิกข้อมูลที่ไม่คงที่ถูกประกาศสำหรับแลมบ์ดาสำหรับตัวแปรที่คัดลอกมาแต่ละตัว
  • ในกรณีเฉพาะแลมบ์ดามีชนิดการปิดซึ่งมีตัวดำเนินการเรียกฟังก์ชันเทมเพลตอินไลน์สาธารณะที่ยอมรับพารามิเตอร์ที่ชื่อ foo

ดังนั้นตรรกะจะทำให้ฉันพูดได้อย่างรวดเร็วก่อนว่าพารามิเตอร์ควรเงาตัวแปรที่จับได้ราวกับว่าใน:

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};

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

เอนทิตีที่ถูกจับดั้งเดิม [... ] ควรถูกทำให้เป็นเงาตามปกติตามกฎขอบเขต

เพราะอย่างนั้นฉันจะบอกว่าเสียงดังพูดถูก


ตามที่อธิบายไว้ข้างต้นการค้นหาในบริบทนี้จะไม่ถูกดำเนินการราวกับว่าเราอยู่ในประเภทการปิดที่ถูกแปลง
Columbo

@ Columbo ฉันกำลังเพิ่มบรรทัดที่ฉันพลาดแม้ว่ามันจะชัดเจนจากเหตุผลนั่นคือเสียงดังที่ถูกต้อง ส่วนที่น่าขำคือฉันพบ [expr.prim.lambda] / 8 ในขณะที่พยายามให้คำตอบ แต่ฉันไม่สามารถใช้งานได้อย่างถูกต้องเหมือนที่คุณเคยทำ นั่นเป็นเหตุผลที่ทุกครั้งที่อ่านคำตอบของคุณจึงมีความสุข ;-)
skypjack
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.