std :: back_inserter cho một std :: set?


94

Tôi đoán đây là một câu hỏi đơn giản. Tôi cần làm điều gì đó như sau:

std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());

Tất nhiên, std::back_inserterkhông hoạt động vì không có push_back. std::insertercũng cần một trình lặp? Tôi chưa sử dụng std::inserternên tôi không chắc phải làm gì.

Có ai có ý tưởng gì không?


Tất nhiên, tùy chọn khác của tôi là sử dụng vector cho s2, và sau đó chỉ cần sắp xếp nó sau. Có lẽ điều đó tốt hơn?

Câu trả lời:


139

setkhông có push_backvì vị trí của một phần tử được xác định bởi bộ so sánh của tập hợp. Sử dụng std::insertervà vượt qua nó .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(), 
          std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

Chèn sau đó lặp sẽ gọi s2.insert(s2.begin(), x)nơi xlà giá trị truyền cho iterator khi ghi vào nó. Tập hợp sử dụng trình vòng lặp như một gợi ý nơi để chèn. Bạn cũng có thể sử dụng s2.end().


2
Vì cũng inserter(vec, vec.end())hoạt động cho vectơ, tại sao mọi người lại sử dụng back_inserter ngay từ đầu?
NHDaly

7
@NHDaly: vì back_inserter nhanh
marton78

@ marton78 Nhưng không phải nó chỉ nhanh hơn với một biên độ rất nhỏ, nếu có? Việc gọi insertthay vì push_backtrên một vectơ phải gần giống (O (1)) khi không cần di chuyển phần tử nào.
Felix Dombek

3
@FelixDombek bạn nói đúng, sẽ không chậm hơn nhiều đâu. v.insert(x, v.end())sẽ có thêm một nhánh ở đầu (do nó di chuyển n phần tử, nhưng ở đây n là 0). Tuy nhiên, việc sử dụng inserter1) truyền đạt một mục đích khác với sử dụng push_back2) là không bình thường và khiến người đọc dừng lại và nghĩ rằng 3) là một sự bi quan quá sớm.
marton78

0

Vào năm 2016, có một đề xuất để có một " insertertrình lặp đối số duy nhất ". https://isocpp.org/files/papers/p0471r0.html . Tôi không thể tìm thấy nó có đề xuất nâng cao hay không. Tôi nghĩ nó có lý.

Bây giờ bạn có thể có hành vi này xác định chức năng trình tạo:

template<class Container>
auto sinserter(Container& c){
    using std::end;
    return std::inserter(c, end(c));
}

Được dùng như:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});

Điều này có hoạt động trên tất cả các vùng chứa tiêu chuẩn không? Nó không hoạt động trên std :: forward_list (lỗi trình biên dịch là "forward_list không có thành viên nào có tên 'insert'", bên trong phần tạo của insert_iterator::operator=). Có nên không?
Don Hatch

@DonHatch, bất kỳ thứ gì có insert(và end). Có vẻ như nó forward_listkhông có inserthoạt động ngay từ đầu insert_after. Và ngay cả khi điều đó được thay đổi, nó không thể được chèn vào sau khi kết thúc, tôi nghĩ. Bạn không thể sử dụng một std::listthay thế?
alfC

Chắc chắn, cá nhân tôi không cần std :: forward_list. Nhưng tôi quan tâm đến câu hỏi chung chung "làm cách nào để sao chép một vùng chứa này sang vùng chứa khác?" cho tất cả các cặp hộp chứa mà nó có ý nghĩa. Mối quan tâm hiện tại của tôi là thực hiện các trình phân bổ vùng chứa chung như short_alloc của @HowardHinnant.
Don Hatch

@DonHatch, vấn đề là có nhiều biến thể cho câu hỏi đó. ("" làm cách nào để sao chép một vùng chứa này sang một vùng chứa khác? "). Ví dụ: bạn có muốn giữ nguyên các giá trị gốc, bạn có muốn giảm thiểu phân bổ, v.v. Đối với trường hợp đơn giản nhất, câu trả lời tốt nhất theo ý kiến ​​của tôi là sử dụng phương thức khởi tạo vùng chứa có hai trình vòng lặp (hầu hết các vùng chứa có thể lấy điều đó) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC

1
Vâng, std :: set không phải là SequentialContainer, và điều đó tốt :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) Rất tuyệt, tôi nghĩ đó là câu trả lời của tôi. Chúc mừng!
Don Hatch
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.