2010-03-13 11 views
7

Tôi có một lớp bao gồm một std :: list và muốn cung cấp public start() và end() cho const_iterator và private start() và end() cho chỉ iterator đơn giản.Làm thế nào để cung cấp stl như container với iterator công cộng const và private-const iterator?

Tuy nhiên, trình biên dịch đang xem phiên bản riêng tư và khiếu nại rằng đó là riêng tư thay vì sử dụng phiên bản công khai const.

Tôi hiểu rằng C++ sẽ không quá tải trên kiểu trả về (trong trường hợp này là const_iterator và iterator) và do đó nó đang chọn phiên bản không const vì đối tượng của tôi không phải là const.

Việc rút ngắn đối tượng của tôi thành const trước khi gọi bắt đầu() hoặc không quá tải tên bắt đầu có cách nào để thực hiện việc này không?

Tôi cho rằng đây là một mẫu đã biết mà mọi người đã giải quyết trước đây và muốn theo dõi cách thức giải quyết vấn đề này.

class myObject { 
public: 
    void doSomethingConst() const; 
}; 

class myContainer { 
public: 
    typedef std::list<myObject>::const_iterator const_iterator; 
private: 
    typedef std::list<myObject>::iterator iterator; 

public: 
    const_iterator begin() const { return _data.begin(); } 
    const_iterator end() const { return _data.end(); } 
    void reorder(); 
private: 
    iterator begin() { return _data.begin(); } 
    iterator end() { return _data.end(); } 
private: 
    std::list<myObject> _data; 
}; 

void myFunction(myContainer &container) { 
    myContainer::const_iterator itr = container.begin(); 
    myContainer::const_iterator endItr = container.end(); 
    for (; itr != endItr; ++itr) { 
    const myObject &item = *itr; 
    item.doSomethingConst(); 
    } 
    container.reorder(); // Do something non-const on container itself. 
} 

Các lỗi từ trình biên dịch là một cái gì đó như thế này:

../../src/example.h:447: error: `std::_List_iterator<myObject> myContainer::begin()' is private 
caller.cpp:2393: error: within this context 
../../src/example.h:450: error: `std::_List_iterator<myObject> myContainer::end()' is private 
caller.cpp:2394: error: within this context 

Cảm ơn.

-William

+0

Theo tôi thấy không có thừa kế trong mã của bạn, có lẽ bạn nên sửa 'Tôi đang phát sinh một lớp riêng tư'. –

+0

Rất tiếc, các chỉnh sửa đã không được đồng bộ hóa, cảm ơn bạn đã chỉ ra rằng nó đã được chỉnh sửa ngay bây giờ. – WilliamKF

+1

Nếu không bắt đầu và kết thúc không phải của bạn là riêng tư đối với vùng chứa của bạn, tại sao bạn muốn cung cấp các chức năng riêng cho chúng? Tại sao không chỉ sử dụng các trình vòng lặp danh sách một cách trực tiếp. – AraK

Trả lời

4

Tôi nghĩ tùy chọn duy nhất của bạn là đổi tên các phương thức riêng tư (nếu bạn cần chúng ngay từ đầu).

Bên cạnh đó tôi tin rằng bạn nên đổi tên typedefs:

class MyContainer 
{ 
public: 
    typedef std::list<Object>::const_iterator iterator; 
    typedef iterator const_iterator; 

    const_iterator begin() const; 
    const_iterator end() const; 

private: 
    typedef std::list<Object>::iterator _iterator; 
    _iterator _begin(); 
    _iterator _end(); 
    ... 
}; 

Container có nghĩa vụ phải typedef cả iteratorconst_iterator. Một hàm chung chấp nhận một cá thể không const của vùng chứa của bạn có thể mong đợi sử dụng kiểu gõ iterator - ngay cả khi nó sẽ không sửa đổi các phần tử. (Ví dụ: BOOST_FOREACH.)

Sẽ tốt hơn khi tính chính xác của const, bởi vì chức năng chung thực sự cố gắng sửa đổi các đối tượng, loại vòng lặp thực (là const_iterator) sẽ không cho phép.

Là một thử nghiệm, sau nên biên dịch với container của bạn:

int main() 
{ 
    myContainer m; 
    BOOST_FOREACH(const myObject& o, m) 
    {} 
} 

Lưu ý rằng m không phải là const, nhưng chúng tôi chỉ cố gắng để có được tài liệu tham khảo const cho các loại chứa, vì vậy đây sẽ được phép.

+0

Arg, đăng vài giây trước tôi! – Potatoswatter

+0

Xem http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier –

+0

@Emile: AFAIK, sử dụng một dấu gạch dưới đơn theo sau là trường hợp ký tự là OK. Cá nhân tôi sẽ không sử dụng phong cách này thực sự. – UncleBens

5

Ý tưởng tồi có nguồn gốc từ std :: list (không được thiết kế để bắt nguồn từ).

Sử dụng biến thành viên thuộc loại std :: list.

class myContainer 
{ 
    std::list<myObject> m_data; 
    public: 

    typedef std::list<myObject>::const_iterator myContainer::const_iterator; 
    private: 
    typedef std::list<myObject>::iterator myContainer::iterator; 

    public: 

    myContainer::const_iterator begin() const 
    { 
     return m_data.begin(); 
    } 

    myContainer::const_iterator end() const 
    { 
     return m_data.end(); 
    } 

    private: 
    myContainer::iterator begin() 
    { 
     return m_data.begin(); 
    } 

    myContainer::iterator end() 
    { 
     return m_data.end(); 
    } 
}; 
+0

Đã cập nhật câu hỏi để phản ánh đề xuất tốt này, mặc dù vấn đề chính vẫn còn. Dường như chỉ có giải pháp là đổi tên riêng hoặc static_cast thành const trước khi gọi hàm start()/end(). – WilliamKF

4

Bạn cần thay đổi tên của đầu bắt đầu riêng tư. Trình biên dịch không thể phân biệt bởi chỉ có kiểu trả về

này làm việc cho tôi: lưu ý tên _begin _end

#include <list> 


class myObject {}; 

class myContainer : private std::list<myObject> { 
public: 
    typedef std::list<myObject>::const_iterator const_iterator; 
private: 
    typedef std::list<myObject>::iterator iterator; 

public: 
    myContainer::const_iterator begin() const { 
    return std::list<myObject>::begin(); 
    } 
    myContainer::const_iterator end() const { 
    return std::list<myObject>::end(); 
    } 
private: 
    myContainer::iterator _begin() { 
    return std::list<myObject>::begin(); 
    } 
    myContainer::iterator _end() { 
    return std::list<myObject>::end(); 
    } 
}; 

void myFunction(myContainer &container) { 
    myContainer::const_iterator aItr = container.begin(); 
    myContainer::const_iterator aEndItr = container.end(); 
    for (; aItr != aEndItr; ++aItr) { 
    const myObject &item = *aItr; 
    // Do something const on container's contents. 
    } 
} 

int main(){ 
    myContainer m; 
    myFunction(m); 
} 
+1

Mặc dù đổi tên sẽ thực hiện thủ thuật ở đây, bạn vẫn gặp khó khăn nếu bạn muốn ném đối tượng của mình vào một hàm chung mong muốn 'begin()' và 'end()' ... – xtofl

+0

Dường như đây là giải pháp thay thế duy nhất khác làm static_cast (container) .begin()/end() cho mỗi người gọi. – WilliamKF

+0

@Emilie Cormier Tôi không biết điều đó. Cảm ơn!. Trong python là một thành ngữ phổ biến để đặt tên cho các thành viên riêng với một _ hàng đầu. @xtofl, nếu các chức năng đó là riêng tư, không ai ngoài chính lớp đó có thể sử dụng nó, bạn đang nói về chức năng chung nào? một ví dụ? – fabrizioM

1

Bạn có thể muốn thay đổi chữ ký của phương pháp MyFunction của bạn như thế này:

void myFunction(const myContainer &container) 

vì phương thức const sẽ chỉ được gọi trên đối tượng const. Điều gì đang xảy ra là bạn đang cố gọi một phương thức không phải là const trong trường hợp của bạn là riêng tư.

+0

Nhưng trong một số trường hợp sẽ không đủ, đã cập nhật testcase để phản ánh điều này. – WilliamKF