Quyền truy cập bản đồ C ++ loại bỏ các giới hạn (const)


113

Đoạn mã sau cho biết rằng việc chuyển bản đồ constvào trong operator[]phương thức sẽ loại bỏ các điều kiện:

#include <iostream>
#include <map>
#include <string>

using namespace std;

class MapWrapper {
public:
    const int &get_value(const int &key) const {
        return _map[key];
    }

private:
    map<int, int> _map;
};

int main() {
    MapWrapper mw;
    cout << mw.get_value(42) << endl;
    return 0;
}

Điều này có phải do sự phân bổ có thể xảy ra trên bản đồ? Không có hàm nào có quyền truy cập bản đồ có thể được khai báo const không?

MapWrapper.cpp:10: error: passing ‘const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >]’ discards qualifiers


Chỉ là một nitpick, nhưng mw có thể được khai báo đơn giản là MapWrapper mw;
luke

Điểm tốt - tôi viết bằng một vài ngôn ngữ nên tôi có xu hướng bình thường hóa cú pháp giữa chúng để tất cả chúng đều vừa vặn trong đầu tôi. :)
cdleary

Tôi có thể đánh giá cao điều đó. Tuy nhiên, hãy cẩn thận, trong những trường hợp như thế này, bạn đã có một cấu trúc và gán đối tượng bổ sung không cần thiết.
luke

Một điểm tốt khác - dựa vào toán tử gán mặc định không phải là một thực tiễn tốt cho các ví dụ công khai. ;)
cdleary

Câu trả lời:


152

std::map's operator []không được khai báo là const, và không thể là do hành vi của nó:

T & operator [] (const Key & key)

Trả về một tham chiếu đến giá trị được ánh xạ tới một khóa tương đương với khóa, thực hiện chèn nếu khóa đó chưa tồn tại.

Do đó, hàm của bạn không thể được khai báo constvà sử dụng bản đồ operator[].

std::map'sfind() chức năng cho phép bạn tìm kiếm một chìa khóa mà không sửa đổi bản đồ.

find()trả về một iteratorhoặc const_iteratorđến một std::pairchứa cả khóa ( .first) và giá trị ( .second).

Trong C ++ 11, bạn cũng có thể sử dụng at()cho std::map. Nếu phần tử không tồn tại, hàm sẽ ném một std::out_of_rangengoại lệ, ngược lại với operator [].


8
bổ sung: VALUE = map.find (KEY) -> thứ hai; Tôi phải biết rằng 'find ()' trả về một trình lặp, thuộc loại cặp.
FlipMcF

5
Tôi muốn nói thêm rằng bây giờ trong C11, bạn có thể sử dụng: std :: map :: at (key) và tránh trình lặp.
Juan Besa

3
Hấp dẫn. Tôi sẽ nghĩ rằng C ++ sẽ phân biệt giữa lvalue operator[](ví dụ foo[bar] = baz) và rvalue operator[](ví dụ x = foo[bar]) - cái sau chắc chắn có thể là const.
Claudiu

15

operator[]không có quá tải đủ điều kiện const, nó không thể được sử dụng an toàn trong một hàm đủ điều kiện const. Điều này có thể là do quá tải hiện tại được xây dựng với mục tiêu vừa trả về vừa thiết lập các giá trị chính.

Thay vào đó, bạn có thể sử dụng:

VALUE = map.find(KEY)->second;

hoặc, trong C ++ 11, bạn có thể sử dụng at()toán tử:

VALUE = map.at(KEY);

map.find(KEY)->second; không an toàn khi các giá trị bản đồ là chuỗi. Nó có xu hướng in rác khi không tìm thấy KEY.
syam

1
Đó là một câu trả lời được giải thích hợp lý đi vào vấn đề. Hôm qua tôi đã dành 2 giờ để tìm hiểu xem chuyện gì đang xảy ra với một trường hợp tương tự. Chúng ta có thể đồng ý rằng thông báo lỗi tốt nhất là gây hiểu lầm không? Tôi có thể là cách rõ ràng hơn nếu nó không có từ 'này' và đã nhắc tới const-Ness thay vì chung chung hơn vòng loại .
carnicer

11

Bạn không thể sử dụng operator[]trên bản đồ constvì phương pháp đó không constcho phép bạn sửa đổi bản đồ (bạn có thể gán cho _map[key]). findThay vào đó, hãy thử sử dụng phương pháp này.


1
Nhân tiện giải thích: toán tử của bản đồ [] phải làm gì nếu khóa không tồn tại? Nếu bản đồ không phải là hằng số, khóa sẽ được thêm vào với giá trị được tạo mặc định. Nếu bản đồ const, điều gì có thể được trả về bởi toán tử []? Không có giá trị nào tại khóa đó.

Đó là một câu trả lời được giải thích hợp lý đi vào vấn đề. Hôm qua tôi đã dành 2 giờ để tìm hiểu xem chuyện gì đang xảy ra với một trường hợp tương tự. Chúng ta có thể đồng ý rằng thông báo lỗi tốt nhất là gây hiểu lầm không? Tôi có thể là cách rõ ràng hơn nếu nó không có từ 'này' và đã nhắc tới const-Ness thay vì chung chung hơn vòng loại .
carnicer

7

Một số phiên bản mới hơn của tiêu đề GCC (4.1 và 4.2 trên máy của tôi) có bản đồ hàm thành viên không chuẩn :: at () được khai báo là const và ném std :: out_of_range nếu khóa không có trong bản đồ.

const mapped_type& at(const key_type& __k) const

Từ một tham chiếu trong nhận xét của hàm, có vẻ như đây đã được đề xuất như một hàm thành viên mới trong thư viện chuẩn.


Tôi đoán đó là một câu hỏi nhỏ. Hàm at là một phần của tiêu chuẩn sắp tới, nhưng tôi không tìm thấy at () trong tiêu chuẩn hiện tại.
Sebastian Mach

'at' là một phần của C ++ 11.
Étienne

0

Đầu tiên, bạn không nên sử dụng các ký hiệu bắt đầu bằng _ vì chúng được dành riêng cho người viết trình biên dịch / triển khai ngôn ngữ. _Map sẽ rất dễ trở thành lỗi cú pháp trên trình biên dịch của ai đó, và bạn sẽ không có ai phải đổ lỗi cho chính mình.

Nếu bạn muốn sử dụng dấu gạch dưới, hãy đặt nó ở cuối chứ không phải ở đầu. Có thể bạn đã mắc phải sai lầm này vì bạn đã thấy một số mã Microsoft thực hiện điều đó. Hãy nhớ rằng, họ viết trình biên dịch của riêng họ, vì vậy họ có thể sử dụng nó. Mặc dù vậy, đó là một ý tưởng tồi.

toán tử [] không chỉ trả về một tham chiếu, nó thực sự tạo mục nhập trong bản đồ. Vì vậy, bạn không chỉ nhận được một ánh xạ, nếu không có, bạn đang tạo một ánh xạ. Đó không phải là những gì bạn dự định.


5
Quan điểm của bạn về _chỉ là sai. Các số nhận dạng bắt đầu bằng hai dấu gạch dưới ( __example) hoặc các số nhận dạng bắt đầu bằng một dấu gạch dưới và một chữ cái viết hoa ( _Example) được dành riêng. _examplekhông được bảo lưu.
Ethan
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.