2009-09-06 17 views
6

Tôi đang cố gắng để tạo ra một "thưa thớt" class vector trong C++, như vậy:điều hành quá tải [] cho một vector thưa thớt

template<typename V, V Default> 
class SparseVector { 
    ... 
} 

Bên trong, nó sẽ được đại diện bởi một std::map<int, V> (nơi V là loại giá trị được lưu trữ). Nếu phần tử không có trong bản đồ, chúng tôi sẽ giả sử rằng phần tử này bằng với giá trị Default từ đối số mẫu.

Tuy nhiên, tôi gặp sự cố khi quá tải toán tử chỉ số, []. Tôi phải quá tải các nhà điều hành [], bởi vì tôi đang đi qua các đối tượng từ lớp này vào một chức năng tăng mà mong đợi [] để hoạt động chính xác.

Phiên bản const đủ đơn giản: kiểm tra xem chỉ mục có trong bản đồ hay không, trả về giá trị của nó nếu có hoặc Default nếu không.

Tuy nhiên, phiên bản không phải const yêu cầu tôi trả lại tham chiếu và đó là nơi tôi gặp sự cố. Nếu giá trị chỉ là đọc, tôi không cần (cũng không muốn) thêm bất kỳ thứ gì vào bản đồ; nhưng nếu nó là được viết, tôi có thể cần phải đặt một mục mới vào bản đồ. Vấn đề là quá tải [] không biết liệu giá trị có đang là đọc hoặc được viết. Nó chỉ trả về một tham chiếu.

Có cách nào để giải quyết vấn đề này không? Hoặc có lẽ để làm việc xung quanh nó?

+2

boost :: mapped_vector <> nên làm điều gì đó tương tự - bạn có thể nghiên cứu ý tưởng đó (hoặc có thể chỉ sử dụng ý tưởng đó). –

+0

Nó không hỗ trợ các giá trị mặc định của tôi, và ngoài ra, tôi sẽ làm điều này cho ma trận hai chiều, do đó, sử dụng nó trực tiếp là ra khỏi câu hỏi. Nhưng vẫn là một tài liệu tham khảo hữu ích! – Thomas

Trả lời

13

Có thể có một số thủ thuật rất đơn giản, nhưng nếu không tôi nghĩ operator[] chỉ phải trả lại thứ gì đó có thể được gán từ V (và chuyển thành V), không nhất thiết là V &. Vì vậy, tôi nghĩ rằng bạn cần phải trả lại một số đối tượng với một quá tải operator=(const V&), mà tạo ra các mục trong container thưa thớt của bạn.

Tuy nhiên, bạn sẽ phải kiểm tra chức năng Boost nào với tham số mẫu của nó - chuyển đổi do người dùng xác định thành V có thể ảnh hưởng đến chuỗi chuyển đổi nào, chẳng hạn như ngăn chặn chuyển đổi do người dùng xác định chuỗi.

+0

Đây là giải pháp duy nhất. Mặt khác, các đối tượng proxy không hoàn hảo - ví dụ, nếu 'V' có bất kỳ toán tử chuyển đổi quá tải nào, thì một proxy sẽ không thể truyền bá chúng một cách liền mạch. –

+0

Ah vừa thử nghiệm các vectơ tăng cường. Có vẻ như họ cũng hành xử theo cách này. Hấp dẫn. –

+0

(Và 'vectơ ' cũng không phải là một container STL.) – sbi

9

Đừng để toán tử không const & triển khai trả lại tham chiếu, nhưng đối tượng proxy. Sau đó bạn có thể thực hiện toán tử gán của đối tượng proxy để phân biệt các truy cập đọc với toán tử [] từ các truy nhập ghi.

Dưới đây là một số phác thảo mã để minh họa ý tưởng. Cách tiếp cận này không đẹp, nhưng tốt - đây là C++. Lập trình viên C++ không lãng phí thời gian cạnh tranh trong các cuộc thi sắc đẹp (họ cũng sẽ không có cơ hội). ;-)

template <typename V, V Default> 
ProxyObject SparseVector::operator[](int i) { 
    // At this point, we don't know whether operator[] was called, so we return 
    // a proxy object and defer the decision until later 
    return ProxyObject<V, Default>(this, i); 
} 

template <typename V, V Default> 
class ProxyObject { 
    ProxyObject(SparseVector<V, Default> *v, int idx); 
    ProxyObject<V, Default> &operator=(const V &v) { 
     // If we get here, we know that operator[] was called to perform a write access, 
     // so we can insert an item in the vector if needed 
    } 

    operator V() { 
     // If we get here, we know that operator[] was called to perform a read access, 
     // so we can simply return the existing object 
    } 
}; 
+0

Có cách nào để có loại chuyển đổi được không, ví dụ: cả một 'const int &' và 'int &', và có trình biên dịch chọn cái cũ bất cứ khi nào nó có thể sử dụng một giá trị không ghi được? Tôi muốn có một đối tượng proxy trả về một tham chiếu đến một trường được nạp biểu mẫu đối tượng chính, và sau đó có bản sao hủy của nó trường quay trở lại đối tượng chính nếu nó có thể đã thay đổi, nhưng tôi không thể tìm ra cách để tránh ghi lại quyền truy cập chỉ đọc. – supercat

1

Tôi tự hỏi liệu thiết kế này có âm thanh hay không.

Nếu bạn muốn trả lại tham chiếu, điều đó có nghĩa là khách hàng của lớp có thể lưu trữ kết quả gọi operator[] trong tài liệu tham khảo và đọc từ/ghi vào bất kỳ lúc nào sau đó. Nếu bạn không trả lại một tham chiếu, và/hoặc không chèn một phần tử mỗi khi một chỉ mục cụ thể được giải quyết, làm thế nào họ có thể làm điều này? (Ngoài ra, tôi có cảm giác rằng tiêu chuẩn yêu cầu một container STL thích hợp cung cấp operator[] để toán tử đó trả về một tham chiếu, nhưng tôi không chắc chắn về điều đó.)

Bạn có thể phá vỡ điều đó bằng cách cung cấp proxy của mình cũng là operator V&() (sẽ tạo mục nhập và gán giá trị mặc định), nhưng tôi không chắc chắn điều này sẽ không mở lỗ vòng khác trong một số trường hợp Tôi chưa nghĩ đến.

std::map giải quyết vấn đề này bằng cách xác định rằng phiên bản không const của toán tử đó luôn chèn phần tử (và không cung cấp phiên bản const).

Tất nhiên, bạn luôn có thể nói điều này không phải là một vùng chứa STL ngoài giá và operator[] không trả lại các tham chiếu đơn giản mà người dùng có thể lưu trữ. Và có lẽ đó là OK. Tôi tự hỏi.

+0

Giới thiệu về tính đúng đắn của thiết kế: đó là tối ưu hóa bộ nhớ của một véc tơ chung, được sử dụng trong các trường hợp cụ thể - vì vậy giao diện/hợp đồng/tài liệu phải bao gồm điều đó. Về tính hợp lệ của các tham chiếu: ngay cả các trình lặp vòng stl không phải lúc nào cũng hợp lệ. – xtofl

+2

@xtofl: tuy nhiên, các container STL xác định rất cẩn thận những hoạt động nào có thể làm mất hiệu lực vòng lặp và/hoặc tham chiếu đến các phần tử của vùng chứa. Vùng chứa này phải làm như vậy và người dùng phải đảm bảo rằng bất kỳ mẫu nào sử dụng lớp làm tham số đều không tạo ra các yêu cầu mà nó không thể đáp ứng. –