Hành vi không xác định trong vectơ đúc


19

Tại sao mã này viết một số lượng không xác định của các số nguyên dường như chưa được khởi tạo?

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


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

Tôi dự kiến ​​đầu ra sẽ được 77 777 7777.

Là mã này được cho là không xác định?

Câu trả lời:


18

vector<vector<int>>{{77, 777, 7777}}là tạm thời và sau đó sử dụng vector<vector<int>>{{77, 777, 7777}}[0]trong ranged-for sẽ là một hành vi không xác định.

Bạn nên tạo một biến đầu tiên, như

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


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

Ngoài ra Nếu bạn sử dụng Clang 10.0.0, nó sẽ đưa ra cảnh báo về hành vi này.

cảnh báo: đối tượng sao lưu con trỏ sẽ bị hủy ở cuối vectơ biểu thức đầy đủ [-Wdangling-gsl]> {{77, 777, 7777}} [0]


2
Vui lòng sử dụng using std::vectorthay vì using namespace std;để ngăn chặn thực hành xấu này lan rộng.
infinitezero

10

Điều này là do vectơ bạn đang lặp đi lặp lại sẽ bị phá hủy trước khi vào vòng lặp.

Đây là những gì thường xảy ra:

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
}

Các vấn đề bắt đầu ở dòng đầu tiên vì nó được đánh giá như sau:

  1. Đầu tiên xây dựng vectơ của vectơ với các đối số đã cho và vectơ đó trở thành tạm thời vì nó không có tên.

  2. Sau đó, tham chiếu phạm vi được liên kết với vectơ được đăng ký sẽ chỉ có hiệu lực miễn là vectơ chứa nó là hợp lệ.

  3. Khi dấu chấm phẩy đạt được, vectơ tạm thời bị phá hủy và trong hàm hủy của nó, nó sẽ phá hủy và phân bổ bất kỳ vectơ được lưu trữ nào bao gồm cả vectơ được đăng ký.

  4. Bạn kết thúc với một tham chiếu đến một vectơ bị phá hủy sẽ được lặp đi lặp lại.

Để tránh vấn đề này, có hai giải pháp:

  1. Khai báo vectơ trước vòng lặp để nó tồn tại cho đến khi phạm vi của nó kết thúc bao gồm vòng lặp.

  2. C ++ 20 đi kèm với một câu lệnh init được cung cấp để giải quyết các vấn đề này và tốt hơn so với cách tiếp cận đầu tiên nếu bạn muốn vectơ bị hủy sau vòng lặp ngay lập tức:

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

Đây không phải là những gì mà điển hình là xảy ra. Hành vi chính xác này (cộng với phạm vi thích hợp và cân nhắc về cách đặt tên) được quy định theo tiêu chuẩn, tuân theo quy tắc as-if.
Konrad Rudolph

Tôi đang đề cập đến cuộc sống. Ngay cả khi bạn viết cùng một mã bằng tay, bạn chỉ có đảm bảo rằng bạn có được hành vi mong muốn và trình biên dịch sẽ làm những gì có thể với tối ưu hóa
dev65

TIL C ++ 20 cú pháp khai báo phạm vi. Không biết nên vui hay buồn.
Tiểu hành tinh có cánh

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

Tôi hy vọng rằng điều này là lơ lửng.

Mặc dù định nghĩa của một phạm vi đảm bảo rằng RHS của dấu hai chấm vẫn "tồn tại" trong suốt thời gian, nhưng bạn vẫn đang đăng ký tạm thời. Chỉ có kết quả của chỉ mục được giữ, nhưng kết quả đó là một tham chiếu và vectơ thực tế có thể tồn tại qua biểu thức đầy đủ trong đó nó được khai báo. Điều đó không mô tả toàn bộ vòng lặp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.