2016-11-24 15 views
17

Tôi có vấn đề sau:Làm thế nào để tránh const cast để truy cập bản đồ?

std::map<A*,double> map; 

void getColor(A const * obj){ 
    double d = map[obj]; // does not compile wihtout const_cast<A*>(obj) 
    // do something 
} 

Tôi có một bản đồ std::map (ở đâu đó) mà các cửa hàng con trỏ đến đối tượng A. Tôi có chức năng getColorkhông thao tác các đối tượng A và do đó sẽ đưa con trỏ đến số const A làm đầu vào.

Chức năng getColor sẽ không biên dịch mà không sử dụng const_cast.

Dàn diễn viên const là một vấn đề thiết kế, nhưng tôi không biết làm thế nào để phá vỡ nó nếu tôi không muốn để làm cho các phím trong mapconst.

Bất kỳ trợ giúp nào được đánh giá cao.

+14

Ngoài sự tò mò: Tại sao bạn không muốn tạo khóa? –

+4

Tại sao bạn không muốn tạo khóa là 'const A *'? – Gonmator

+0

liên quan: http://stackoverflow.com/a/17639764/4181011 –

Trả lời

18

Có hai kịch bản có thể ở đây:

  1. Chức năng biết/hy vọng rằng obj đã có mặt trên bản đồ, và bạn đang sử dụng [] cho thuận tiện.

  2. Bạn đang sử dụng [] để có đầy đủ tiềm năng, nghĩa là bạn mong đợi thêm obj vào bản đồ nếu chưa có.

Trong trường hợp 2, bạn gặp lỗi trong chữ ký getColor. Vì nó có khả năng có thể vượt qua obj đến một nơi mà nó sẽ được lưu trữ như A*, nó là sai cho nó để chấp nhận một chỉ const A*. Lưu ý rằng ngay cả khi một hàm không sửa đổi một đối tượng chính nó nhưng chuyển nó vào một nơi nào đó mà nó có thể được sửa đổi, nó có hiệu quả sửa đổi nó gián tiếp và do đó nên lấy nó như là không const.

Trong trường hợp 1, nó phụ thuộc vào phiên bản C++ của bạn. C++ 14 đã giới thiệu một hàm template overload of find và các chức năng thành viên có liên quan là std::map, mất bất kỳ thứ gì có thể so sánh với Key thay vì chỉ Key. Do đó bạn có thể thay đổi chức năng như thế này:

void getColor(A const * obj){ 
    doubel d = map.find(obj)->second; 
    // do something 
} 

Lưu ý rằng để làm việc này, bạn cũng cần phải thay đổi loại của bản đồ sử dụng một so sánh trong suốt: std::map<A*,double, std::less<>> map; (như đầu tiên chỉ ra bởi @Leon's answer).

Nếu bạn đang mắc kẹt với C++ 11 hoặc cũ hơn, bạn đã hết may mắn và bạn sẽ phải sống với const_cast. Lưu ý rằng với một chú thích thích hợp, const_cast là hoàn toàn an toàn và được chấp nhận trong trường hợp này (chưa kể đến cách duy nhất để tiếp tục mà không thay đổi loại map). Một lần nữa, bạn nên sử dụng find hoặc có lẽ at thay vì [], vì bạn không muốn chèn vào bản đồ.

+0

Có 'ít <>' làm điều đúng bảo đảm khi thông qua con trỏ? – Yakk

+0

@Yakk Có, không giống như 'toán tử <', 'std :: less' được đảm bảo hoạt động ngay cả đối với con trỏ đối với các đối tượng không liên quan. – Angew

+1

@angew "các chuyên môn cho bất kỳ loại con trỏ nào mang lại tổng số thứ tự, ngay cả khi các toán tử dựng sẵn' <', '> ',' <=', '> = 'không. và 'less_equal ', nếu nhà điều hành cuộc gọi gọi một toán tử tích hợp so sánh con trỏ, toán tử cuộc gọi sẽ tính tổng thứ tự. " ok, tiêu chuẩn rõ ràng ở đây! – Yakk

6

Nếu bạn có thể đủ khả năng chuyển sang C++ 14 thì bạn có thể cấu hình bản đồ của bạn để sử dụng một transparent comparator (điều này sẽ làm việc kể từ khi một con trỏ const có thể được so sánh với một con trỏ không const):

std::map<A*,double, std::less<>> map; 
//     ^^^^^^^^^^^ 
//     enable transparent comparator on this map 

void getColor(A const * obj){ 
    auto it = map.find(obj); 
    assert(it != map.end()); 
    double d = it->second; 
    // do something 
} 

Lưu ý rằng bạn sẽ phải sử dụng std::map::find() thay vì std::map::operator[], kể từ sau doesn't have a transparent version.

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