2012-05-14 24 views
24

Tôi hơi bối rối bởi ngữ nghĩa của std::map::insert. Ý tôi là, tôi không phàn nàn - tiêu chuẩn là tiêu chuẩn và API là như vậy. Tuy nhiên,Lý do cho bản đồ std của C++ chèn ngữ nghĩa?

insert sẽ

kiểm tra hoạt động chèn cho mỗi phần tử chèn dù yếu tố khác đã tồn tại trong container với cùng giá trị quan trọng , nếu như vậy, yếu tố này không được chèn vào và giá trị ánh xạ của nó không phải là đã thay đổi theo bất kỳ cách nào.

Và - chỉ trong phiên bản đối số đơn pair<iterator,bool> insert (const value_type& x); thậm chí nó sẽ cho bạn biết liệu nó có chèn giá trị (mới, có thể khác) vào khóa hay không. Theo như tôi hiểu, các phiên bản trình lặp sẽ tự động bỏ qua chèn nếu khóa đã tồn tại.

Đối với tôi, điều này chỉ đơn giản là truy cập trực quan, Tôi đã dự kiến ​​phần giá trị sẽ bị ghi đè và phần giá trị cũ sẽ bị hủy khi chèn. Rõ ràng, các nhà thiết kế của STL nghĩ khác nhau - bất cứ ai biết lý do (lịch sử) hoặc có thể đưa ra một giải thích thấu đáo về cách ngữ nghĩa hiện tại tạo ra (ý nghĩa) hơn?

By dụ:

Có một vài cách cơ bản để thực hiện chèn trong một bản đồ duy nhất-key như std::map:

  • chèn, thay thế nếu đã tồn tại
  • chèn, bỏ qua nếu đã tồn tại (đây là hành vi của std :: map)
  • chèn, ném lỗi nếu đã tồn tại
  • chèn, UB nếu đã tồn tại

bây giờ tôi đang cố gắng để hiểu tại sao insert_or_ignore có ý nghĩa hơn insert_or_replace (hoặc insert_or_error)!


Tôi nhìn vào bản sao của tôi TC++PL (tiếc là tôi chỉ có phiên bản tiếng Đức), và thú vị, Stroustrup viết trong chương 17.4.1.7 (hoạt động danh sách cho đồ): (dịch thô xin lỗi từ Đức)

(...) Thông thường, người ta không quan tâm cho dù một chìa khóa (sic!) là mới chèn hoặc đã tồn tại trước khi cuộc gọi đến insert() (...)

Mà, có vẻ như với tôi, sẽ chỉ đúng với đặt và không cho bản đồ, bởi vì đối với bản đồ, nó sẽ tạo ra một số khác biệt nếu giá trị được cung cấp được chèn hoặc giá trị cũ vẫn còn trong bản đồ . (Nó rõ ràng không quan trọng đối với chìa khóa, vì nó là tương đương.)


Lưu ý: Tôi biết về operator[] và tôi biết về mục 24 của Effective STL và có đề xuất efficientAddOrUpdate chức năng. Tôi chỉ tò mò cho một lý do thành ngữ nghĩa của insert bởi vì cá nhân tôi thấy chúng phản trực giác.

+5

Vâng, bạn không yêu cầu sửa đổi giá trị hiện tại, bạn đã yêu cầu chèn giá trị (mới). Tôi đồng ý rằng báo cáo thất bại nhất quán sẽ là một điều tốt. Bạn vẫn có thể dereference iterator trả về và kiểm tra xem giá trị mới hoặc giá trị cũ có hiện diện hay thậm chí sử dụng trình lặp đó để cập nhật giá trị hiện tại. –

+2

Nếu bạn muốn thay thế/tạo, sử dụng toán tử '[]'. – BoBTFish

+0

Dưới đây là một số mã để ["chèn mạnh"] (http://stackoverflow.com/a/8337563/596781) vào bản đồ. –

Trả lời

5

Tôi không biết về lý do chính thức nhưng tôi sẽ lưu ý tính hai mặt với operator[].

Có vẻ như rõ ràng rằng người ta sẽ thích hai hương vị của chèn:

  • hoàn toàn phụ
  • phụ/phá hoại

Nếu chúng ta thấy một map như một đại diện thưa thớt của một mảng, thì sự hiện diện của operator[] có ý nghĩa. Tôi không biết liệu từ điển tồn tại từ trước có tồn tại hay không và quyết định cú pháp này (có thể, tại sao không).

Ngoài ra, tất cả Vùng chứa STL có một số tình trạng quá tải insert và sự tương đồng về giao diện này là gì cho phép Lập trình chung.

Do đó, chúng tôi có ít nhất hai ứng cử viên cho API: operator[]insert.

Bây giờ, trong C++, nếu bạn đọc:

array[4] = 5; 

nó được naturaly rằng nội dung của các tế bào ở index 4 đã được cập nhật triệt tiêu. Như vậy, tự nhiên là map::operator[] phải trả lại tham chiếu để cho phép cập nhật phá hoại này.

Tại thời điểm này, bây giờ chúng tôi cũng cần phiên bản phụ gia thuần túy và chúng tôi có phương pháp insert này nằm xung quanh. Tại sao không ?

Tất nhiên ai có thể cho insert ngữ nghĩa giống như operator[] và sau đó đi trước và thực hiện một phương pháp insert_or_ignoretrên. Điều này sẽ có được nhiều công việc mặc dù.

Vì vậy, trong khi tôi đồng ý rằng nó có thể là đáng ngạc nhiên, tôi nghĩ rằng lập luận của tôi là không quá thiếu sót và có thể là một lời giải thích khả năng trong các trường hợp đó dẫn chúng ta đây :)


Về lựa chọn thay thế bạn đề xuất :

  • chèn, UB nếu đã tồn tại

May mắn thay, nó không phải là!

  • chèn, lỗi ném nếu đã tồn tại

Chỉ Java (và các dẫn xuất) là ngoại lệ-điên. C++ đã được hình thành trong một thời gian mà các trường hợp ngoại lệ được sử dụng trong các trường hợp ngoại lệ.

  • chèn, thay thế nếu đã tồn tại
  • chèn, bỏ qua nếu đã tồn tại (đây là hành vi của std :: bản đồ)

Chúng tôi đồng ý rằng sự lựa chọn là giữa một những. Lưu ý rằng mặc dù map đã chọn tùy chọn thứ hai, nhưng không hoàn toàn bỏ qua thực tế là mục đã tồn tại, ít nhất là trong phiên bản mục đơn vì nó cảnh báo bạn rằng mục không được chèn.

+0

+1, mặc dù tôi không chắc chắn rằng chúng tôi thực sự * cần * một hàm phụ thuộc hoàn toàn, vì chúng ta luôn có thể gọi 'find()' trước tiên. (với cả hai 'insert' hoặc/hoặc' op [] ') Nhưng có lẽ tính hai mặt là/là một lý do chính đáng. –

+0

@Martin: Tôi đồng ý rằng một 'tìm kiếm 'sẽ làm việc để tạo ra một hàm phụ gia thuần túy. Nó sẽ có yêu cầu một số nồi hơi-tấm mặc dù. –

+0

Vâng, thú vị đủ, tương tự boilerplate (hoặc helper fn) bạn cần bây giờ cho một hiệu quả insert_or_replace :-) –

0

Từ insert() bạn không mong đợi các đối tượng hiện có trong vùng chứa được chạm vào. Đó là lý do tại sao nó đơn giản không chạm vào chúng.

+1

Không trả lời * tại sao nó không báo cáo lỗi? * Một phần của câu hỏi, nhưng có nó trả lời một phần của câu hỏi. Hành vi của hàm khá trực quan, mong đợi một cuộc gọi 'chèn' để sửa đổi hiện tại các yếu tố sẽ khá trực quan. –

+2

Không có hành vi phạm tội, nhưng bạn đang vẹt lại các ngữ nghĩa được xác định của hàm, không cung cấp lý do. * Tại sao tôi không mong đợi các yếu tố hiện có được chạm vào? Bởi vì nó được gọi là "chèn" và không phải là "insert_or_replace" hoặc những gì? Một lần nữa, * đối với tôi * nó phản trực giác, và nó không giúp tôi hiểu khi nào tôi nói "không, không phải". :-) –

+0

@Martin: Sẽ không 'insert_or_ignore' (như trong SQLite) là một tên tốt hơn so với' insert_or_replace'? – dan04

0

pair<iterator,bool> < - phần bool không cho biết liệu chèn có thành công hay không?

Bạn chỉ có thể cập nhật phần giá trị của trình lặp lặp được trả về nếu phần bool là false để cập nhật mục hiện có bằng cùng một khóa.

+0

Câu hỏi đã đề cập đến điều này. Bạn đã bỏ qua phần câu hỏi đã đề cập đến bốn quá tải khác của 'std :: map :: insert'. –

7

Phương thức chèn không chỉ là những gì bạn đang tìm kiếm, nó có vẻ như ... Phương pháp chèn được thực hiện để làm đúng những gì tên ngụ ý ... chèn các giá trị. Tôi đồng ý rằng khả năng tạo ra một giá trị nếu không có, và thay thế giá trị nếu không có ý nghĩa quan trọng trong một số trường hợp, nhưng ở một số trường hợp khác, bạn sẽ không xử lý ngoại lệ, trả về giá trị, v.v. chỉ muốn chèn giá trị nếu giá trị chưa có.

Có vẻ như phương pháp bạn đang tìm kiếm (như BoBTFish đã nêu ở trên) có thể là toán tử []. Chỉ cần sử dụng nó như vậy:

myMap["key"] = "value"; 

Thao tác này sẽ đi qua bản đồ và tìm khóa "chìa khóa" và thay thế giá trị tương ứng bằng "giá trị". Nếu khóa không có ở đó, nó sẽ tạo ra nó. Cả hai phương pháp đều rất hữu ích trong các tình huống khác nhau, và tôi đã tìm thấy bản thân mình bằng cách sử dụng cả hai chỉ phụ thuộc vào những gì tôi cần.

+6

Tôi đã chỉnh sửa bài đăng của bạn. Đầu tiên, không ký (rõ ràng), nó được cau mày (người dùng của bạn vẫn còn nhìn thấy được); thứ hai, vui lòng kiểm tra cú pháp đánh dấu để tìm hiểu cách định dạng đoạn mã nội tuyến và đoạn mã nhiều dòng. Cảm ơn câu trả lời của bạn và chào mừng bạn trên SO :) –

2

Tôi không yêu cầu biết lý do ban đầu cho quyết định đó, nhưng không quá khó để đưa ra quyết định. Tôi nghĩ ;-)

Hành vi hiện tại của "chèn hoặc bỏ qua" giúp dễ dàng triển khai hai cách khác - ít nhất là đối với những người không ở trên tạo và sử dụng các chức năng không phải thành viên để bổ sung chức năng thư viện chuẩn ("nó không đủ OOP-y!").

Ví dụ (bằng văn bản ngay tại chỗ, vì vậy lỗi có thể có mặt):

template<typename Map> 
void insert_or_update(Map& map, typename Map::value_type const& x) 
{ 
    std::pair<typename Map::iterator, bool> result = map.insert(x); 
    if (!result.second) 
    result.first->second = x.second; // or throw an exception (consider using 
            // a different function name, though) 
} 

Do lưu ý rằng như là, chức năng trên không thực sự khác biệt nhiều so operator[] - vâng, nó tránh được khởi tạo mặc định , nhưng đồng thời (vì tôi lười biếng) nó không tận dụng được ngữ nghĩa di chuyển mà STL cập nhật của bạn có thể đã hỗ trợ cho operator[].Tuy nhiên, bất kỳ hành vi chèn nào khác đối với map sẽ khiến việc thực hiện những thứ khác trở nên tẻ nhạt hơn vì map::find chỉ trả về một dấu kết thúc nếu khóa chưa có trong bản đồ. Với sự giúp đỡ của <algorithm> (và đặc biệt là lower_bound), tất nhiên, vẫn có thể viết chức năng phụ kiện thực hiện mà không làm chết chúng chi tiết triển khai và cấu trúc chung xấu xí như vòng ;-).

Các vấn đề liên quan