Tại sao toán tử [] không phải là const cho bản đồ STL?


89

Ví dụ có sẵn, vì lợi ích của câu hỏi:

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map[x] << std::endl
}

Điều này sẽ không biên dịch, vì toán tử [] không phải là const.

Điều này thật không may, vì cú pháp [] trông rất rõ ràng. Thay vào đó, tôi phải làm điều gì đó như sau:

void MyClass::MyFunction( int x ) const
{
  MyMap iter = m_map.find(x);
  std::cout << iter->second << std::endl
}

Điều này đã luôn luôn làm phiền tôi. Tại sao toán tử [] không phải là const?


5
Điều gì sẽ operator[]mang lại trong trường hợp phần tử đã cho không tồn tại?
Frerich Raabe

4
@Frerich Raabe: Điều tương tự như ở hàm thành viên: ném std :: out_of_range
Jean-Simon Brochu

Câu trả lời:


90

Đối với std::mapstd::unordered_map, operator[]sẽ chèn giá trị chỉ mục vào vùng chứa nếu trước đó nó không tồn tại. Nó hơi không trực quan, nhưng đó là cách của nó.

Vì nó phải được phép không thành công và chèn một giá trị mặc định, nên toán tử không thể được sử dụng trên một constphiên bản của vùng chứa.

http://en.cppreference.com/w/cpp/container/map/operator_at


3
std::setkhông có operator[].
avakar 25/09/09

2
Đó là câu trả lời đúng, nhưng một phiên bản const có thể làm điều tương tự như thành viên "at". Đó là ném một std :: out_of_range ...
Jean-Simon Brochu

Khi được sử dụng để đọc một giá trị, không có giá trị mặc định nào để cung cấp. std::vectorcó một toán tử đọc []đó là const. mapcũng nên làm như vậy.
wcochran

51

Bây giờ với C ++ 11, bạn có thể có một phiên bản sạch hơn bằng cách sử dụng tại ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}

4
Nếu mapcó const và không const at()s - tại sao không giống nhau đối với operator[]? với phiên bản const không chèn bất cứ thứ gì mà là ném? (Hoặc trả lại một tùy chọn, khi std :: tùy chọn làm cho nó thành tiêu chuẩn)
einpoklum

@einpoklum Điểm của tính đúng của const chủ yếu là kiểm tra thời gian biên dịch tĩnh. Tôi muốn trình biên dịch phàn nàn hơn là ném một ngoại lệ vì tôi đã không sử dụng các đối tượng const một cách chính xác.
Millie Smith

@einpoklum Rất muộn, nhưng đối với những độc giả khác: có hai lần quá tải làm những việc khác nhau như vậy sẽ thật khủng khiếp. Lý do duy nhất atcó hai kiểu là vì nó có a return *this;, và sự khác biệt duy nhất giữa các lần quá tải là const-ness của tham chiếu được trả về. Tác dụng thực tế của cả hai atđều giống nhau hoàn toàn (nghĩa là không có tác dụng).
HTNW

28

Lưu ý cho độc giả mới.
Câu hỏi ban đầu là về các vùng chứa STL (không cụ thể về std :: map)

Cần lưu ý rằng có một phiên bản const của toán tử [] trên hầu hết các vùng chứa.
Chỉ là std :: map và std :: set không có phiên bản const và đây là kết quả của cấu trúc bên dưới thực hiện chúng.

Từ std :: vector

reference       operator[](size_type n) 
const_reference operator[](size_type n) const 

Ngoài ra đối với ví dụ thứ hai của bạn, bạn nên kiểm tra lỗi không tìm thấy phần tử.

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}

1
std::setkhông có operator[]ở tất cả.
Mọi người

2

Vì toán tử [] có thể chèn một phần tử mới vào vùng chứa, nó không thể là một hàm thành viên const. Lưu ý rằng định nghĩa của toán tử [] cực kỳ đơn giản: m [k] tương đương với (* ((m.insert (value_type (k, data_type ()))). 1)). Thứ hai. Nói một cách chính xác, chức năng thành viên này là không cần thiết: nó chỉ tồn tại để thuận tiện


0

Toán tử chỉ mục chỉ nên là const cho một vùng chứa chỉ đọc (không thực sự tồn tại trong STL mỗi ngày).

Toán tử chỉ mục không chỉ được sử dụng để xem xét các giá trị.


6
Câu hỏi đặt ra là, tại sao nó không có hai phiên bản quá tải - một const, một phiên bản khác không const- chẳng hạn std::vector.
Pavel Minaev 25/09/09

-2

Nếu bạn khai báo biến thành viên std :: map của mình là có thể thay đổi

mutable std::map<...> m_map;

bạn có thể sử dụng các hàm thành viên không phải const của std :: map trong các hàm thành viên const của bạn.


15
Tuy nhiên, đây là một ý tưởng khủng khiếp.
GManNickG 25/09/09

7
API cho lớp của bạn nói dối nếu bạn làm điều đó. Hàm tuyên bố rằng nó là const - nghĩa là nó sẽ không sửa đổi bất kỳ biến thành viên nào - nhưng trong thực tế, nó có thể đang sửa đổi thành viên dữ liệu m_map.
Runcible

2
mutablecó thể được sử dụng cho các thành viên thích std::mutex, bộ nhớ đệm và trình trợ giúp gỡ lỗi. Nếu bản đồ được sử dụng như một bộ nhớ cache để tăng tốc một consthàm "getter" rất tốn kém , thì mutablecó thể chấp nhận được. Bạn cần phải cẩn thận, nhưng nó không phải là một ý tưởng khủng khiếp.
Mark Lakata
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.