Danh sách khởi tạo bên trong std :: cặp


26

Mã này:

#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;
}

biên dịch nhưng trả về segfault. Tại sao?

Đã thử nghiệm trên gcc 8.3.0 và trên trình biên dịch trực tuyến.


1
Để thuận tiện: Godbolt liên kết không có std::pair .
Max Langhof

Câu trả lời:


24

std::initializer_listkhông có nghĩa là được lưu trữ, nó chỉ có nghĩa là ... khởi tạo tốt. Trong nội bộ, nó chỉ lưu trữ một con trỏ đến phần tử đầu tiên và kích thước. Trong mã của bạn, các std::stringđối tượng là tạm thời và initializer_listkhông sở hữu chúng, không kéo dài cuộc sống của chúng, không sao chép chúng (vì nó không phải là vật chứa) nên chúng ra khỏi phạm vi ngay sau khi tạo, nhưng bạn initializer_listvẫn giữ một con trỏ cho chúng. Đó là lý do tại sao bạn nhận được lỗi phân khúc.

Để lưu trữ, bạn nên sử dụng một container, như std::vectorhoặc std::array.


Nó làm phiền tôi rằng điều này có thể biên dịch được. Ngôn ngữ ngớ ngẩn :(
Các cuộc đua nhẹ nhàng trong quỹ đạo

1
@LightnessRaceswithMonica Tôi có rất nhiều thịt bò với initializer_list. Không thể sử dụng các đối tượng chỉ di chuyển, vì vậy bạn không thể sử dụng danh sách init với vector của unique_ptr chẳng hạn. Kích thước của initializer_listkhông phải là hằng số thời gian biên dịch. Và thực tế là std::vector<int>(3)std::vector<int>{3}làm những điều hoàn toàn khác nhau. Làm tôi buồn :(
bolov 4/12/19


3

Tôi sẽ chỉ thêm một chút chi tiết. Một mảng cơ bản của các std::initializer_listhành vi tương tự như thời gian. Hãy xem xét các lớp sau:

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

và cách sử dụng của nó trong đoạn mã sau:

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

Nó in ra

ctor
dtor
barrier

vì ở dòng đầu tiên, một thể hiện tạm thời của loại Xđược tạo ra (bằng cách chuyển đổi hàm tạo từ 1) và cũng bị hủy. Các tài liệu tham khảo được lưu trữ vào psau đó được treo lủng lẳng.

Đối với std::initializer_list, nếu bạn sử dụng nó theo cách này:

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

sau đó, mảng bên dưới (tạm thời) tồn tại miễn là lthoát. Do đó, đầu ra là:

ctor
ctor
barrier
dtor
dtor

Tuy nhiên, nếu bạn chuyển sang

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

Đầu ra là một lần nữa

ctor
dtor
barrier

vì mảng bên dưới (tạm thời) chỉ tồn tại ở dòng đầu tiên. Hủy bỏ con trỏ đến các yếu tố lsau đó dẫn đến hành vi không xác định.

Bản demo trực tiếp là đây .

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.