เหตุใดฉันจึงจับสิ่งนี้โดยอ้างอิง ('& this') ในแลมบ์ดาไม่ได้


93

ฉันเข้าใจวิธีที่ถูกต้องในการจับภาพthis(เพื่อแก้ไขคุณสมบัติของวัตถุ) ในแลมบ์ดามีดังนี้:

auto f = [this] () { /* ... */ };

แต่ฉันอยากรู้เกี่ยวกับลักษณะเฉพาะต่อไปนี้ที่ฉันเคยเห็น:

class C {
    public:
        void foo() {
            // auto f = [] () { // this not captured
            auto f = [&] () { // why does this work?
            // auto f = [&this] () { // Expected ',' before 'this'
            // auto f = [this] () { // works as expected
                x = 5;
            };
            f();
        }

    private:
        int x;
};

ความแปลกที่ฉันสับสน (และต้องการคำตอบ) คือสาเหตุที่ทำให้เกิดผลดังต่อไปนี้:

auto f = [&] () { /* ... */ }; // capture everything by reference

และทำไมฉันไม่สามารถจับภาพthisโดยอ้างอิงได้อย่างชัดเจน:

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.

6
ทำไมคุณถึงต้องการ ? ในแง่ของสิ่งที่มีการอ้างอิงถึงตัวชี้อาจเคยกลัวจะมีประโยชน์: thisไม่สามารถเปลี่ยนแปลงได้ก็ไม่มากพอที่จะทำให้การอ้างอิงได้เร็วขึ้น ... และอยู่แล้ว , มันไม่ได้มีอยู่จริงจึงมี ไม่มีชีวิตจริงหมายถึงการอ้างอิงใด ๆ ที่จะห้อยตามคำจำกัดความ thisเป็น prvalue ไม่ใช่ lvalue
underscore_d

คำตอบ:


112

สาเหตุที่ใช้[&this]ไม่ได้เนื่องจากเป็นข้อผิดพลาดทางไวยากรณ์ แต่ละพารามิเตอร์คั่นด้วยลูกน้ำในlambda-introducerคือ a capture:

capture:
    identifier
    & identifier
    this

คุณจะเห็นว่า&thisไม่อนุญาตให้ใช้ในเชิงไวยากรณ์ เหตุผลที่ไม่อนุญาตเนื่องจากคุณไม่ต้องการจับภาพthisโดยการอ้างอิงเนื่องจากเป็นตัวชี้ const ขนาดเล็ก คุณแค่อยากจะส่งผ่านค่ามันเท่านั้นดังนั้นภาษาจึงไม่รองรับการจับthisโดยการอ้างอิง

ในการจับภาพthisอย่างชัดเจนคุณสามารถใช้[this]เป็นไฟล์lambda-introducer.

สิ่งแรกcaptureสามารถเป็นcapture-defaultซึ่งคือ:

capture-default:
    &
    =

ซึ่งหมายถึงการจับภาพสิ่งที่ฉันใช้โดยอัตโนมัติโดยการอ้างอิง ( &) หรือตามค่า ( =) ตามลำดับ - อย่างไรก็ตามการรักษาthisเป็นแบบพิเศษในทั้งสองกรณีจะถูกจับโดยค่าด้วยเหตุผลที่ให้ไว้ก่อนหน้านี้ (แม้จะมีการจับค่าเริ่มต้น&ซึ่งโดยปกติจะหมายถึง จับโดยการอ้างอิง)

5.1.2.7/8:

สำหรับวัตถุประสงค์ในการค้นหาชื่อ (3.4) การกำหนดประเภทและค่าของthis(9.3.2) และการแปลง id- นิพจน์ที่อ้างถึงสมาชิกคลาสที่ไม่คงที่เป็นนิพจน์การเข้าถึงสมาชิกคลาสโดยใช้(*this)(9.3.1), คำสั่งประกอบ [OF THE LAMBDA] ได้รับการพิจารณาในบริบทของแลมด้า - นิพจน์

ดังนั้นแลมบ์ดาจึงทำหน้าที่ราวกับว่ามันเป็นส่วนหนึ่งของฟังก์ชันสมาชิกที่ปิดล้อมเมื่อใช้ชื่อสมาชิก (เช่นในตัวอย่างของคุณการใช้ชื่อx) ดังนั้นมันจะสร้าง "การใช้โดยนัย" thisเหมือนกับที่ฟังก์ชันสมาชิกทำ

ถ้าแลมบ์ดาจับรวมถึงการจับภาพเริ่มต้นที่เป็นตัวระบุในแลมบ์ดาจับต้องไม่นำหน้าด้วย& &ถ้าแลมบ์ดาจับรวมถึงการจับภาพเริ่มต้นที่เป็น=ที่แลมบ์ดาจับต้องไม่ประกอบด้วย และแต่ละตัวบ่งชี้จะมีจะนำหน้าด้วยthis &ตัวระบุหรือthisจะต้องไม่ปรากฏมากกว่าหนึ่งครั้งในการจับแลมบ์ดา

ดังนั้นคุณสามารถใช้[this], [&], [=]หรือ[&,this]เป็นlambda-introducerที่จะจับthisตัวชี้โดยค่า

อย่างไรก็ตาม[&this]และ[=, this]มีรูปร่างไม่ดี ในกรณีสุดท้าย gcc จะเตือนอย่างไม่น่าให้อภัยสำหรับ[=,this]สิ่งนั้นexplicit by-copy capture of ‘this’ redundant with by-copy capture defaultมากกว่าข้อผิดพลาด


3
@KonradRudolph: จะเกิดอะไรขึ้นถ้าคุณต้องการจับภาพบางสิ่งตามมูลค่าและอื่น ๆ โดยการอ้างอิง? หรือต้องการให้ชัดเจนมากกับสิ่งที่คุณจับ?
Xeo

2
@KonradRudolph: เป็นคุณลักษณะด้านความปลอดภัย คุณอาจจับชื่อที่คุณไม่ได้ตั้งใจได้โดยบังเอิญ
Andrew Tomazos

8
@KonradRudolph: โครงสร้างระดับบล็อกไม่ได้คัดลอกตัวชี้ไปยังวัตถุที่พวกเขาใช้เป็นประเภทที่ไม่ระบุตัวตนที่มองไม่เห็นแบบใหม่ซึ่งจะสามารถอยู่รอดจากขอบเขตการปิดล้อมได้โดยใช้ชื่อวัตถุในนิพจน์ การจับแลมด้าเป็นธุรกิจที่อันตรายมากกว่า
Andrew Tomazos

5
@KonradRudolph ฉันจะบอกว่า "ใช้[&]ถ้าคุณกำลังทำอะไรบางอย่างเช่นการสร้างบล็อกเพื่อส่งผ่านไปยังโครงสร้างควบคุม" แต่จะจับภาพได้ชัดเจนว่าคุณกำลังผลิตแลมด้าที่จะใช้เพื่อวัตถุประสงค์ที่เรียบง่ายน้อยกว่า [&]เป็นความคิดที่น่าสยดสยองหากแลมด้าจะมีอายุยืนยาวกว่าขอบเขตปัจจุบัน อย่างไรก็ตามการใช้ lambdas หลายอย่างเป็นเพียงวิธีการส่งผ่านบล็อกเพื่อควบคุมโครงสร้างและบล็อกจะไม่อยู่ได้นานกว่าบล็อกที่สร้างขึ้นในขอบเขต
Yakk - Adam Nevraumont

2
@Ruslan: ไม่ใช่thisเป็นคำหลักthisไม่ใช่ตัวระบุ
Andrew Tomazos

7

เนื่องจากมาตรฐานไม่มี&thisในรายการจับภาพ:

N4713 8.4.5.2 การจับภาพ:

lambda-capture:
    capture-default
    capture-list
    capture-default, capture-list

capture-default:
    &
    =
capture-list:
    capture...opt
    capture-list, capture...opt
capture:
    simple-capture
    init-capture
simple-capture:
    identifier
    &identifier
    this
    * this
init-capture:
    identifier initializer
    &identifier initializer
  1. สำหรับวัตถุประสงค์ในการดักจับแลมบ์ดานิพจน์อาจอ้างอิงถึงเอนทิตีในพื้นที่ดังนี้:

    7.3 นิพจน์นี้อาจอ้างถึง * สิ่งนี้

ดังนั้นการรับประกันมาตรฐานthisและ*thisถูกต้องและ&thisไม่ถูกต้อง นอกจากนี้ยังมีการจับภาพthisวิธีการจับ*this(ซึ่งเป็น lvalue วัตถุเอง) โดยอ้างอิง , มากกว่าการจับthisตัวชี้โดยค่า !


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