แลมบ์ดาจับภาพอ้างอิง const


166

มันเป็นไปได้ที่จะจับโดยอ้างอิง const ในการแสดงออกแลมบ์ดา?

ฉันต้องการให้การบ้านที่ระบุไว้ด้านล่างล้มเหลวตัวอย่างเช่น:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

อัปเดต:เนื่องจากเป็นคำถามเก่ามันอาจจะดีถ้าหากมีสิ่งอำนวยความสะดวกใน C ++ 14 เพื่อช่วยในเรื่องนี้ ส่วนขยายใน C ++ 14 อนุญาตให้เราจับภาพวัตถุที่ไม่ใช่ const โดยอ้างอิง const หรือไม่ ( สิงหาคม 2558 )


ไม่ควรดูแลมบ์ดาของคุณเช่น: [&, &best_string](string const s) { ...}?
erjot

3
การจับที่ไม่สอดคล้องกันจริงๆ "const &" จะมีประโยชน์มากเมื่อคุณมีวัตถุ const ขนาดใหญ่ซึ่งควรเข้าถึง แต่ไม่ได้รับการแก้ไขในฟังก์ชั่นแลมบ์ดา
sergtk

ดูรหัส คุณสามารถใช้แลมบ์ดาพารามิเตอร์สองตัวและผูกตัวที่สองเป็นการอ้างอิงแบบ const มาพร้อมกับค่าใช้จ่าย
อเล็กซ์

1
นี่เป็นไปไม่ได้ใน C ++ 11 ที่ดูเหมือนจะเป็น แต่บางทีเราสามารถอัปเดตคำถามนี้สำหรับ C ++ 14 - มีส่วนขยายที่อนุญาตหรือไม่ แลมบ์ดา C ++ 14 ทั่วไปจับได้หรือไม่
Aaron McDaid

คำตอบ:


127

const ไม่ได้อยู่ในไวยากรณ์สำหรับการดักจับตั้งแต่ n3092:

capture:
  identifier
  & identifier
  this

ข้อความกล่าวถึงการดักจับโดยการคัดลอกและการดักจับโดยอ้างอิงและไม่ได้กล่าวถึงการเรียงลำดับใด ๆ

รู้สึกเหมือนเป็นการกำกับดูแลฉัน แต่ฉันไม่ได้ทำตามกระบวนการมาตรฐานอย่างใกล้ชิด


47
ฉันเพียงแค่การติดตามข้อผิดพลาดกลับไปยังตัวแปรที่ถูกดัดแปลงมาจากการจับภาพที่เป็นที่แน่นอน constแต่ควรจะได้รับ หรือมากกว่านั้นอย่างถูกต้องหากตัวแปรการจับภาพconstคอมไพเลอร์จะบังคับใช้พฤติกรรมที่ถูกต้องในโปรแกรมเมอร์ [&mutableVar, const &constVar]มันต้องการจะดีถ้าไวยากรณ์การสนับสนุน
ฌอน

ดูเหมือนว่าสิ่งนี้น่าจะเป็นไปได้สำหรับ C ++ 14 แต่ฉันไม่สามารถใช้งานได้ ข้อเสนอแนะใด ๆ
Aaron McDaid

38
Constness นั้นสืบทอดมาจากตัวแปรที่จับได้ ดังนั้นถ้าคุณต้องการที่จะจับaเป็นconstประกาศconst auto &b = a;ก่อนแลมบ์ดาและจับb
StenSoft

7
@StenSoft Bleargh ยกเว้นจะเห็นได้ชัดว่าสิ่งนี้ไม่ได้ใช้เมื่อจับสมาชิกตัวแปรโดยการอ้างอิง: [&foo = this->foo]ภายในconstฟังก์ชั่นทำให้ฉันมีข้อผิดพลาดที่ระบุว่าการจับตัวเองทิ้งตัวระบุ นี่อาจเป็นข้อผิดพลาดใน GCC 5.1 แต่ฉันคิดว่า
Kyle Strand

119

ใน ใช้static_cast/ const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

การสาธิต


ใน ใช้std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

ตัวอย่าง 2


นอกจากนี้อาจแก้ไขได้ในคำตอบที่ยอมรับหรือไม่ ทั้งสองวิธีควรมีคำตอบที่ดีหนึ่งคำซึ่งครอบคลุมทั้ง c ++ 11 และ c ++ 14 แม้ว่าฉันคิดว่ามันอาจจะเป็นที่ถกเถียงกันอยู่ว่า c ++ 14 จะดีพอสำหรับทุกคนในช่วงหลายปีที่ผ่านมา
Aaron McDaid

12
@AaronMcDaid const_castโดยไม่มีเงื่อนไขสามารถเปลี่ยนวัตถุที่ระเหยได้เป็นวัตถุ const (เมื่อถูกขอให้โยนconst) ดังนั้นสำหรับการเพิ่มข้อ จำกัด ที่ฉันชอบstatic_cast
Piotr Skotnicki

1
@PiotrSkotnicki ในทางกลับกันstatic_castเพื่ออ้างอิง const อาจสร้างแบบชั่วคราวหากคุณไม่ได้รับประเภทที่ถูกต้อง
MM

24
@MM &basic_string = std::as_const(best_string)ควรแก้ปัญหาทั้งหมด
Piotr Skotnicki

15
@PiotrSkotnicki ยกเว้นปัญหาของการว่าเป็นวิธีที่น่าเกลียดที่จะเขียนบางสิ่งบางอย่างที่ควรconst& best_stringจะเป็นง่ายๆเป็น
Kyle Strand

13

ฉันคิดว่าส่วนการจับภาพไม่ควรระบุconstเนื่องจากการดักจับหมายถึงต้องการวิธีเข้าถึงตัวแปรขอบเขตด้านนอกเท่านั้น

ตัวระบุจะระบุได้ดีกว่าในขอบเขตด้านนอก

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

ฟังก์ชั่นแลมบ์ดาเป็น const (ไม่สามารถเปลี่ยนค่าในขอบเขตของมัน) ดังนั้นเมื่อคุณจับตัวแปรโดยค่าตัวแปรไม่สามารถเปลี่ยนแปลงได้ แต่การอ้างอิงไม่ได้อยู่ในขอบเขตแลมบ์ดา


1
@ Amarnath Balasubramani: มันเป็นเพียงความคิดเห็นของฉันฉันคิดว่าไม่จำเป็นต้องระบุ const อ้างอิงในแลมบ์ดาจับส่วนทำไมต้องมีตัวแปร const ที่นี่และไม่ const ที่อื่น (ถ้าเป็นไปได้มันจะผิดพลาดได้ง่าย) ) มีความสุขที่จะเห็นการตอบสนองของคุณต่อไป
zhb

2
หากคุณต้องการแก้ไขbetter_stringภายในขอบเขตที่มีอยู่โซลูชันนี้จะไม่ทำงาน กรณีการใช้งานสำหรับการจับภาพเป็น const-ref คือเมื่อตัวแปรจำเป็นต้องเปลี่ยนแปลงในขอบเขตที่มี แต่ไม่ใช่ภายในแลมบ์ดา
Jonathan Sharman

@JanathanSharman คุณไม่ต้องเสียค่าใช้จ่ายในการสร้าง const อ้างอิงถึงตัวแปรดังนั้นคุณสามารถสร้าง a const string &c_better_string = better_string;และส่งผ่านไปยัง lambda อย่างมีความสุข:[&c_better_string]
Steed

@Steed ปัญหาที่คุณกำลังแนะนำชื่อตัวแปรพิเศษในขอบเขตโดยรอบ ฉันคิดว่าวิธีแก้ปัญหาของ Piotr Skotnicki ด้านบนนั้นสะอาดที่สุดเพราะได้ค่าความถูกต้องคงที่และทำให้ขอบเขตตัวแปรน้อยที่สุด
Jonathan Sharman

@ JonathanSharman ที่นี่เราเข้าสู่ดินแดนแห่งความคิดเห็น - อะไรคือสิ่งที่สวยที่สุดหรือสะอาดที่สุดหรืออะไรก็ตาม ประเด็นของฉันคือทั้งสองวิธีมีความเหมาะสมกับงาน
ม้า

8

ฉันเดาว่าคุณไม่ได้ใช้ตัวแปรเป็นพารามิเตอร์ของ functor คุณควรใช้ระดับการเข้าถึงของฟังก์ชั่นปัจจุบัน หากคุณคิดว่าคุณไม่ควรแยกแลมบ์ดาออกจากฟังก์ชั่นนี้มันไม่ได้เป็นส่วนหนึ่งของมัน

อย่างไรก็ตามคุณสามารถบรรลุสิ่งเดียวกันกับที่คุณต้องการโดยใช้การอ้างอิง const อื่นแทน:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

แต่นั่นก็เหมือนกับการสมมติว่าแลมบ์ดาของคุณจะต้องถูกแยกออกจากฟังก์ชั่นปัจจุบันทำให้มันไม่ใช่แลมบ์ดา


1
ข้อจับยังคงกล่าวถึงbest_stringเท่านั้น นอกจากนั้น GCC 4.5 "สำเร็จปฏิเสธ" รหัสเช่นตั้งใจ
sellibitze

ใช่สิ่งนี้จะให้ผลลัพธ์ที่ฉันพยายามที่จะบรรลุในระดับเทคนิค อย่างไรก็ตามท้ายที่สุดคำตอบสำหรับคำถามเดิมของฉันดูเหมือนจะเป็น "ไม่"
John Dibling

ทำไมถึงทำให้มันเป็น "non-lambda"?

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

3
"ถ้านักแสดงควรเป็นคนที่มีบริบทให้ทำให้เป็นนักดนตรีตัวจริง" ... และจูบลาอินไลน์ที่เป็นไปได้?
Andrew Lazarus

5

ฉันคิดว่าคุณมีสามตัวเลือกที่แตกต่างกัน:

  • อย่าใช้การอ้างอิง const แต่ใช้การดักจับการคัดลอก
  • ไม่สนใจความจริงที่ว่ามันแก้ไขได้
  • ใช้ std :: bind เพื่อผูกอาร์กิวเมนต์หนึ่งตัวของฟังก์ชันเลขฐานสองซึ่งมีการอ้างอิง const

ใช้สำเนา

ส่วนที่น่าสนใจเกี่ยวกับ lambdas ที่มีการถ่ายสำเนาคือพวกมันอ่านได้อย่างเดียวเท่านั้นและทำในสิ่งที่คุณต้องการ

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

ใช้ std :: ผูก

std::bindลด arity ของฟังก์ชัน อย่างไรก็ตามโปรดทราบว่าสิ่งนี้อาจ / จะนำไปสู่การเรียกใช้ฟังก์ชันทางอ้อมผ่านตัวชี้ฟังก์ชัน

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

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


0

ใช้เสียงดังกราวหรือรอจนกระทั่งข้อผิดพลาด gcc นี้ได้รับการแก้ไข: ข้อผิดพลาด 70385: การดักจับ Lambda โดยการอ้างอิง const อ้างอิงล้มเหลว [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]


1
ในขณะที่ลิงค์นี้อาจตอบคำถามได้ดีกว่าที่จะรวมส่วนที่สำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง คำตอบเฉพาะลิงก์อาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง”
Div

ตกลงฉันแก้ไขคำตอบของฉันเพื่อเพิ่มคำอธิบายข้อผิดพลาด gcc ที่นี่
user1448926

นี่เป็นคำตอบที่ค่อนข้างตรงไปตรงมาถ้ามี ข้อผิดพลาดคือเกี่ยวกับวิธีคอมไพเลอร์ล้มเหลวเมื่อจับสิ่งที่ const ดังนั้นบางทีทำไมวิธีการที่อยู่หรือแก้ไขปัญหาในคำถามอาจไม่ทำงานกับ gcc
สไตน์

0

การใช้ const จะทำให้อัลกอริธึมแอมเปอร์แซนด์ตั้งค่าสตริงเป็นค่าดั้งเดิมหรืออีกนัยหนึ่งแลมบ์ดาจะไม่นิยามตัวเองเป็นพารามิเตอร์ของฟังก์ชันแม้ว่าขอบเขตโดยรอบจะมีตัวแปรพิเศษ ... โดยไม่ต้องกำหนด แม้ว่ามันจะไม่ได้กำหนดสตริงเป็น [&, & best_string] ทั่วไป (string const s) ดังนั้นมันน่าจะดีกว่าถ้าเราปล่อยไว้ที่นั้นพยายามจับภาพการอ้างอิง


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