พฤติกรรมที่ไม่ได้กำหนดในเวกเตอร์ของเวกเตอร์ที่ส่ง


19

ทำไมรหัสนี้จึงเขียนจำนวนเต็มที่ไม่ได้กำหนดจำนวนที่ดูเหมือนไม่ได้กำหนดไว้?

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    for (int i : vector<vector<int>>{{77, 777, 7777}}[0])
        cout << i << ' ';
}

77 777 7777ผมคาดว่าการส่งออกจะเป็น

รหัสนี้ควรถูกยกเลิกหรือไม่?

คำตอบ:


18

vector<vector<int>>{{77, 777, 7777}}เป็นสิ่งชั่วคราวและจากนั้นการใช้vector<vector<int>>{{77, 777, 7777}}[0]ranged-for จะเป็นพฤติกรรมที่ไม่ได้กำหนด

คุณควรสร้างตัวแปรก่อนเช่น

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    auto v = vector<vector<int>>{{77, 777, 7777}};
    for(int i: v[0])
        cout << i << ' ';
}

นอกจากนี้ถ้าคุณใช้ Clang 10.0.0 มันจะให้คำเตือนเกี่ยวกับพฤติกรรมนี้

คำเตือน: การสำรองข้อมูลตัวชี้จะถูกทำลายในตอนท้ายของเวกเตอร์แบบเต็ม [-Wdangling-gsl]> {{77, 777, 7777}} [0]


2
กรุณาใช้using std::vectorแทนusing namespace std;เพื่อป้องกันการปฏิบัติที่ไม่ดีนี้แพร่กระจาย
infinitezero

10

เนื่องจากเวกเตอร์ที่คุณกำลังวนซ้ำจะถูกทำลายก่อนที่จะเข้าสู่ลูป

นี่คือสิ่งที่มักจะเกิดขึ้น:

auto&& range = vector<vector<int>>{{77, 777, 7777}}[0];
auto&& first = std::begin(range);
auto&& last = std::end(range);
for(; first != last; ++first)
{
    int i = *first;
    // the rest of the loop
}

ปัญหาเริ่มต้นที่บรรทัดแรกเนื่องจากมีการประเมินดังนี้:

  1. ประการแรกสร้างเวกเตอร์ของเวกเตอร์ด้วยอาร์กิวเมนต์ที่กำหนดและเวกเตอร์นั้นกลายเป็นชั่วคราวเพราะไม่มีชื่อ

  2. จากนั้นการอ้างอิงช่วงจะถูกผูกไว้กับเวกเตอร์ที่ห้อยซึ่งจะใช้ได้ตราบเท่าที่เวกเตอร์ที่มีนั้นถูกต้อง

  3. เมื่อถึงอัฒภาคเวกเตอร์ชั่วคราวจะถูกทำลายและใน destructor มันจะทำลายและยกเลิกการจัดสรรเวกเตอร์ใด ๆ ที่เก็บไว้ซึ่งรวมถึงหนึ่งห้อย

  4. คุณท้ายด้วยการอ้างอิงถึงเวกเตอร์ที่ถูกทำลายซึ่งจะวนซ้ำ

เพื่อหลีกเลี่ยงปัญหานี้มีสองวิธี:

  1. ประกาศเวกเตอร์ก่อนการวนซ้ำดังนั้นมันจะคงอยู่จนกระทั่งขอบเขตสิ้นสุดซึ่งรวมถึงการวนซ้ำ

  2. C ++ 20 มาพร้อมกับคำสั่ง init ซึ่งจัดทำขึ้นเพื่อแก้ไขปัญหาเหล่านี้และดีกว่าวิธีแรกถ้าคุณต้องการให้เวกเตอร์ถูกทำลายหลังจากวนซ้ำทันที:

    for (vector<vector<int>> vec{{77, 777, 7777}}; int i : vec[0])
    {
    }
    

นี่ไม่ใช่สิ่งที่ "ปกติ" เกิดขึ้น พฤติกรรมที่แน่นอนนี้ (รวมถึงการกำหนดขอบเขตที่เหมาะสมและข้อควรพิจารณาเกี่ยวกับการตั้งชื่อ) ได้รับคำสั่งจากมาตรฐานภายใต้กฎ as-if
Konrad Rudolph

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

TIL C ++ 20 คือการประกาศช่วงสำหรับไวยากรณ์ ไม่แน่ใจว่าจะมีความสุขหรือเศร้า
Asteroids With Wings

6
vector<vector<int>>{{77, 777, 7777}}[0]

ฉันคาดหวังว่านี่จะเป็นอันตราย

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

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