รายการ Initializer ภายใน std :: pair


26

รหัสนี้:

#include <iostream>
#include <string>

std::pair<std::initializer_list<std::string>, int> groups{ { "A", "B" }, 0 };

int main()
{
    for (const auto& i : groups.first)
    {
        std::cout << i << '\n';
    }
    return 0;
}

คอมไพล์ แต่ส่งคืน segfault ทำไม?

ทดสอบกับ gcc 8.3.0 และคอมไพเลอร์ออนไลน์


1
เพื่อความสะดวก: Godbolt เชื่อมโยงกับและโดยไม่ต้อง std::pair
Max Langhof

คำตอบ:


24

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

สำหรับการจัดเก็บที่คุณควรใช้ภาชนะที่ชอบหรือstd::vectorstd::array


มันทำให้ฉันรำคาญใจว่าสิ่งนี้สามารถคอมไพล์ได้ ภาษา Silly :(
การแข่งขัน Lightness ใน Orbit

1
@LightnessRaceswithMonica initializer_listฉันมีจำนวนมากที่มีเนื้อวัว ไม่สามารถใช้วัตถุแบบย้ายเท่านั้นดังนั้นคุณจึงไม่สามารถใช้ list init กับ vector ของ unique_ptr ได้ ขนาดของinitializer_listไม่ใช่ค่าคงที่เวลาคอมไพล์ และความจริงนั้นstd::vector<int>(3)และstd::vector<int>{3}ทำสิ่งที่แตกต่างอย่างสิ้นเชิง ทำให้ฉันเศร้า :(
bolov


3

ฉันจะเพิ่มรายละเอียดอีกเล็กน้อย อาร์เรย์พื้นฐานของstd::initializer_listพฤติกรรมชนิดของเช่นเดียวกับขมับ พิจารณาคลาสต่อไปนี้:

struct X
{
   X(int i) { std::cerr << "ctor\n"; }
   ~X() { std::cerr << "dtor\n"; }
};

และการใช้งานในรหัสต่อไปนี้:

std::pair<const X&, int> p(1, 2);
std::cerr << "barrier\n";

มันพิมพ์ออกมา

ctor
dtor
barrier

ตั้งแต่ที่บรรทัดแรกอินสแตนซ์ชั่วคราวของชนิด Xจะมีการสร้าง (โดยแปลงคอนสตรัคเตอร์จาก1) และทำลายเช่นกัน การอ้างอิงที่เก็บไว้pนั้นห้อยต่องแต่ง

ส่วน std::initializer_listถ้าคุณใช้วิธีนี้:

{
   std::initializer_list<X> l { 1, 2 };
   std::cerr << "barrier\n";
}

ดังนั้นอาร์เรย์ (ชั่วคราว) จะมีอยู่ตราบเท่าที่ lออก ดังนั้นผลลัพธ์คือ:

ctor
ctor
barrier
dtor
dtor

อย่างไรก็ตามหากคุณเปลี่ยนไปใช้

std::pair<std::initializer_list<X>, int> l { {1}, 2 };
std::cerr << "barrier\n";

ผลลัพธ์เป็นอีกครั้ง

ctor
dtor
barrier

เนื่องจากอาร์เรย์ (ชั่วคราว) อยู่ในบรรทัดแรกเท่านั้น การอ้างอิงตัวชี้ไปยังองค์ประกอบจากlนั้นส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด

สาธิต Live เป็นที่นี่

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