Làm thế nào để di chuyển hiệu quả (một số) vật phẩm từ một std :: map sang một cái khác?


8

Tôi có hai std::map<>đối tượng abmuốn di chuyển ( extract+ insert) một số phần tử (nút) từ bản đồ này sang bản đồ khác dựa trên một số vị ngữp .

for (auto i = a.begin(); i != a.end(); ++i)
    if (p(*i))
        b.insert(a.extract(i))

Mã này segfaults trong tiếng kêu. Tôi giả sử vấn đề là sự gia tăng củai sau khi nút của nó được trích xuất từ ​​a.

Là cách đúng / duy nhất để khắc phục điều này bằng cách sử dụng tăng sau?, Ví dụ:

for (auto i = a.begin(); i != a.end();)
    if (p(*i))
        b.insert(a.extract(i++))
    else
        ++i;

EDIT : Tôi đã xóa phần về "tại sao điều này hoạt động trong gcc?", Vì tôi không thể sao chép phần này trên thiết lập hiện tại của mình. Tôi tin rằng nó đã từng xảy ra tại một số thời điểm nhưng với gcc 9.2.1, tôi gặp bế tắc (thay vì segfault). Dù bằng cách nào, tăng sau khi extract()không hoạt động.


2
Liên quan đến, hoặc bản sao của: stackoverflow.com/questions/6438086/iterator-invalidation-rules
Eljay

3
@Eljay Theo tôi, nút bản đồ mới xử lý API ghép nối trong C ++ 17 đủ chuyên môn để đảm bảo câu hỏi của chính nó. Tôi hy vọng điều này không bị đóng như là một bản sao.
NicholasM

Có thể trùng lặp các phần tử Xóa từ std :: set trong khi lặp . std::setstd::maprất giống nhau, và theo như tôi có thể nói extractcó cùng hàm ý vô hiệu như erase.
François Andrieux

Phiên bản nào của clang và gcc bạn đã sử dụng? Đối với tôi, sử dụng clang 8.0 và gcc 7.4, cả hai đều dẫn đến một segfault.
Balázs Kovacsics

Tôi ngạc nhiên rằng mã này sẽ làm việc trong bất kỳ trình biên dịch. Bạn không xử lý sự vô hiệu gây ra bởi trích xuất
Iman Kianrostami

Câu trả lời:


6

Tôi giả sử vấn đề là sự gia tăng của i sau khi nút của nó được trích xuất từ ​​a.

Thật. Trích xuất làm mất hiệu lực các trình vòng lặp cho phần tử được trích xuất và ilà trình vòng lặp như vậy. Hành vi tăng hoặc gián tiếp thông qua một trình vòng lặp không hợp lệ là không xác định.

Tại sao điều này dường như làm việc trong gcc nhưng không phải trong tiếng kêu?

Bởi vì hành vi của chương trình là không xác định.

Là cách đúng / duy nhất để khắc phục điều này với mức tăng sau?

Đó là một cách đúng đắn để khắc phục điều này. Đó không phải là một cách đặc biệt xấu. Nếu bạn không muốn lặp lại gia số, một cách tiếp cận là sử dụng một biến:

for (auto i = a.begin(); i != a.end();) {
    auto current = i++;
    if (p(*current)) {
        // moving is probably unnecessary
        b.insert(a.extract(std::move(current)));
    }
}

Đó là một cách tốt, (một cách hợp lý) giả định rằng sao chép trạng thái của trình vòng lặp ít tốn kém hơn so với sao chép nút.
Spencer

@Spencer sao chép một iterator thường là tầm thường. Nhưng tôi đã thêm một động thái chỉ trong trường hợp.
eerorika

@Spencer currentsẽ bị bỏ lại trong một di chuyển từ tiểu bang. Dù trạng thái đó là gì cũng không thành vấn đề vì nó không còn được sử dụng sau đó nữa.
eerorika

@eeroika Cảm ơn, tôi đã xem mã của bạn kỹ hơn một chút và nhận ra điều đó.
Spencer

Tôi thích biến cục bộ của bạn tốt hơn là có hai lần lặp nhưng tôi sẽ đề xuất một cải tiến nhỏ: giới hạn phạm vi currentbằng cách sử dụng if (auto current = ++i; p(*current))cú pháp c ++ 17s .
axxel
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.