2016-08-04 17 views
26

Hãy xem xét các chương trình đơn giản dưới đây, mà cố gắng để lặp qua các giá trị của một bộ sử dụng tài liệu tham khảo NON-const đến các yếu tố trong đó:Tại sao std :: set có vẻ ép buộc sử dụng bộ const_iterator?

#include <set> 
#include <iostream> 

class Int 
{ 
public: 
    Int(int value) : value_(value) {} 
    int value() const { return value_; } 
    bool operator<(const Int& other) const { return value_ < other.value(); } 
private: 
    int value_; 
}; 

int 
main(int argc, char** argv) { 
    std::set<Int> ints; 
    ints.insert(10); 
    for (Int& i : ints) { 
     std::cout << i.value() << std::endl; 
    } 
    return 0; 
} 

Khi biên dịch này, tôi nhận được một lỗi từ gcc:

test.c: In function ‘int main(int, char**)’: 
test.c:18:18: error: invalid initialization of reference of type ‘Int&’ from expression of type ‘const Int’ 
for (Int& i : ints) { 
      ^ 

Có, tôi biết tôi không thực sự cố gắng sửa đổi các phần tử trong vòng lặp for. Nhưng vấn đề là tôi sẽ có thể có được một tham chiếu không const để sử dụng bên trong vòng lặp, vì bản thân thiết lập không phải là const đủ điều kiện. Tôi nhận được cùng một lỗi nếu tôi tạo một hàm setter và sử dụng nó trong vòng lặp.

+0

Tôi đã giải thích điều này một cách chi tiết, ở đây: [lỗi: đi xxx như 'này' đối số của vứt bỏ xxx vòng loại] (http://stackoverflow.com/questions/ 5923427/error-passing-xxx-as-this-argument-of-xxx-discards-qualifiers) – Nawaz

+0

Nếu bạn thực sự muốn thay đổi một phần tử của 'std :: set', tại chỗ, bạn có thể sử dụng' const_cast'. Chỉ cần chắc chắn _ rằng sửa đổi không thay đổi thứ tự của phần tử trong tập hợp hoặc bạn sẽ chạy vào hành vi không xác định. Điều này là rất không an toàn, đó là lý do tại sao bạn phải đi _way_ trên con đường của bạn để làm điều đó. – gnzlbg

Trả lời

35

Tập hợp giống như bản đồ không có giá trị, chỉ có các phím. Vì các khóa này được sử dụng cho một cây tăng tốc hoạt động trên tập hợp, chúng không thể thay đổi. Vì vậy tất cả các phần tử phải là const để giữ cho các ràng buộc của cây bên dưới bị phá vỡ.

+1

Tôi thấy rằng, nhưng tôi có thể sửa đổi một cái gì đó trong phần tử đã đặt mà không sửa đổi thứ tự sắp xếp của nó trong tập hợp; [http://www.cplusplus.com/reference/set/set/begin](http://www.cplusplus.com/reference/set/set/begin) tuyên bố rằng một bộ không phải const sẽ trả về một không const iterator từ begin() – atomicpirate

+5

std :: set không thể biết rằng nó làm cho mọi thứ 'const' trở nên an toàn. Nếu cấu trúc của bạn có dữ liệu có thể thay đổi và một số trường khóa không thể thay đổi, thì có thể bạn nên sử dụng bản đồ có cấu trúc làm giá trị và bản sao của các trường khóa không thay đổi làm khóa. Bạn luôn có thể const_cast đi các const nếu bạn biết bạn không thay đổi thứ tự của các giá trị, nhưng tôi nghĩ rằng phương pháp tiếp cận bản đồ là sạch hơn. – nate

+5

Theo http://en.cppreference.com/w/cpp/container/set trong khi 'begin' không trả về' iterator' mà iterator là một bí danh cho 'const_iterator' kể từ C++ 11 – nate

4

Hành vi theo thiết kế.

Cung cấp cho bạn trình lặp không-const có thể truyền cảm hứng cho bạn thay đổi phần tử trong tập hợp; hành vi lặp tiếp theo sau đó sẽ không được xác định.

Lưu ý rằng tiêu chuẩn C++ nói rằng set<T>::iteratorconst do đó, kiểu cũ C++ 11 vẫn không hoạt động.

8

std::set sử dụng giá trị chứa để tạo cấu trúc dữ liệu nhanh (thường là cây đỏ đen). Thay đổi giá trị nghĩa là toàn bộ cấu trúc cần được thay đổi. Vì vậy, buộc const ness, std::set ngăn bạn đẩy nó vào trạng thái không thể sử dụng được.

+0

"không thể sử dụng", như trong "sự cố ngẫu nhiên khi bạn cố gắng sử dụng". – Yakk

+2

@Yakk nhiều khả năng nó chỉ không tìm thấy các yếu tố trên một số hoạt động, được cho là tồi tệ hơn. – OrangeDog

7

Từ cpp reference:

In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.

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