2010-10-28 43 views
10

Tôi muốn biết liệu có thể gọi hàm không phải thành viên const từ một hàm thành viên const hay không. Trong ví dụ dưới đây Đầu tiên cho một lỗi trình biên dịch. Tôi hiểu lý do tại sao nó cho một lỗi, tôi muốn biết nếu có một cách để làm việc xung quanh nó.Gọi hàm không phải là thành viên const từ hàm thành viên const

class Foo 
{ 
    const int& First() const 
    { 
     return Second(); 
    } 

    int& Second() 
    { 
     return m_bar; 
    } 

    int m_bar; 
} 

Tôi không thực sự muốn thảo luận về sự khôn ngoan khi làm điều này, tôi tò mò nếu nó thậm chí có thể.

+3

Bạn không phải là người đầu tiên: http://stackoverflow.com/questions/856542/elegant-solution-to-duplicate-const-and-non-const-getters –

+0

cảm ơn Cho đến khi chưa đến tìm kiếm của tôi – Steve

Trả lời

28
return (const_cast<Foo*>(this))->Second(); 

Sau đó, khóc, lặng lẽ.

+0

Điều gì nếu các chức năng có cùng tên? const int & GetBar() const; int & GetBar(); – phandinhlan

+0

Bạn có thể quá tải một chức năng trên constness, do đó, nó sẽ chỉ đơn giản là làm việc. – user3063349

10

Đó là thể:

const int& First() const 
{ 
    return const_cast<Foo*>(this)->Second(); 
} 

int& Second() { return m_bar; } 

tôi sẽ không khuyên này; nó xấu xí và nguy hiểm (bất kỳ việc sử dụng nào của const_cast đều nguy hiểm).

Tốt hơn là di chuyển nhiều chức năng phổ biến như bạn có thể vào các hàm trợ giúp, sau đó có các hàm thành viên const và không phải thành viên của bạn làm ít công việc khi cần.

Trong trường hợp của một accessor đơn giản như thế này, nó chỉ là dễ dàng để return m_bar; từ cả hai chức năng vì nó là để gọi một chức năng từ khác.

+0

Hoặc chỉ cần bật 'int & Second()' vào 'int & Second() const'. Các hàm thành viên không phải là 'const' sẽ không có vấn đề gì khi gọi 'Second()', nhưng bây giờ các hàm thành viên 'const' sẽ có thể gọi nó mà không có bất kỳ jiggery-pokery nào. –

+0

@Jeremy: Làm thế nào để bạn cho rằng chúng tôi trả về tham chiếu không phải const cho một thành viên? – GManNickG

+0

@GMan: Touché. Bạn không thể khai báo 'm_bar' là' int 'có thể thay đổi được. Khuyến nghị của @ Lars Larson là khá tốt, nhưng hướng nó dẫn - viết mỗi accessor may mắn hai lần, một khi 'const'-sửa đổi và một lần không - không phải là rất vui vẻ. –

3

Theo định nghĩa của const, một hàm không được sửa đổi trạng thái của một đối tượng. Nhưng nếu nó gọi một thành viên khác không phải là const, trạng thái của đối tượng có thể bị thay đổi, vì vậy nó không được phép.

Tôi biết bạn đã nói bạn không muốn nghe về điều này, nhưng tôi nghĩ điều đó quan trọng đối với những người khác xảy ra khi đặt câu hỏi.

2

quá tải trên const:

const int& Second() const 
{ 
    return m_bar; 
} 

Bạn có thể thêm phương pháp này và giữ phiên bản không const gốc.

1

trình lặp tương tự trong điều này và tạo một nghiên cứu thú vị.

Biến lặp const thường là cơ sở cho các biến lặp 'không const', và bạn thường sẽ tìm thấy các phôi kiểu const_cast<>() hoặc C được sử dụng để loại bỏ const khỏi lớp cơ sở với các trình truy cập ở trẻ.

Edit: Comment đã

Tôi có một iterator zip nơi một const được thừa hưởng từ không const

này thường sẽ là cấu trúc thừa kế sai (nếu bạn nói những gì tôi nghĩ rằng bạn đang có), lý do là trẻ em không nên ít hạn chế hơn cha mẹ.

nói rằng bạn đã có một số thuật toán lấy trình lặp mã zip của mình, liệu có thích hợp để chuyển một trình lặp const tới một biến không?

nếu bạn có vùng chứa const, chỉ có thể yêu cầu nó cho trình lặp const, nhưng sau đó trình biến đổi const bắt nguồn từ trình lặp để bạn chỉ sử dụng các tính năng trên cha mẹ để có quyền truy cập không const.

Dưới đây là một phác thảo nhanh chóng thừa kế gợi ý theo mô hình stl truyền thống

class ConstIterator: 
    public std::_Bidit< myType, int, const myType *, const mType & > 
{ 
    reference operator*() const { return m_p; } 
} 

class Iterator : public ConstIterator 
{ 
    typedef ConstIterator _Mybase; 
    // overide the types provided by ConstIterator 
    typedef myType * pointer; 
    typedef myType & reference; 

    reference operator*() const 
    { 
    return ((reference)**(_Mybase *)this); 
    } 
} 

typedef std::reverse_iterator<ConstIterator> ConstReverseIterator; 
typedef std::reverse_iterator<Iterator> ReverseIterator; 
+0

Tôi có xu hướng viết các trình vòng lặp như các lớp mẫu và các đặc điểm kiểu được sử dụng để thao tác các độ chói của các hàm thành viên; nó tránh sự cần thiết của 'const_cast' và làm cho mã dễ đọc hơn. –

+0

Âm thanh thú vị, mặc dù nó cho phép sử dụng trình vòng lặp nơi bạn có thể sử dụng const_iterator? Tôi đang thực hiện quan sát ban đầu này về (các) stl tôi đã thấy - chủ yếu là những người có MSVC. –

+0

đây thực sự là trường hợp sử dụng của tôi cho việc này. Tôi có một iterator zip nơi const một kế thừa từ phi const. @ James, bạn có thể trả lời câu hỏi này với các câu trả lời về đặc điểm kiểu? Tôi thực sự quan tâm đến việc không làm điều này nếu có thể – Steve

0

tôi thấy mình cố gắng để gọi một hàm thành viên không const được thừa hưởng, nhưng thực sự là const vì API tôi sử dụng. Cuối cùng tôi đã giải quyết trên một giải pháp khác: thỏa thuận lại API để hàm tôi kế thừa là đúng. Không phải lúc nào cũng có thể thương lượng thay đổi chức năng của người khác, nhưng làm như vậy khi có thể có vẻ sạch hơn và đẹp hơn cần phải sử dụng const_cast và nó cũng mang lại lợi ích cho người dùng khác.

3

Hạn chế của các phương thức thành viên const đến từ thời gian biên dịch. Nếu bạn có thể đánh lừa trình biên dịch, thì có.

class CFoo 
{ 
public: 
    CFoo() {m_Foo = this;} 
    void tee(); 

    void bar() const 
    { 
     m_Foo->m_val++; // fine 
     m_Foo->tee(); // fine 
    } 
private: 
    CFoo * m_Foo; 
    int m_Val; 

}; 

Điều này thực sự xóa mục đích của hàm thành viên const, vì vậy tốt hơn là không nên làm khi thiết kế một lớp mới. Không có gì hại khi biết rằng có một cách để làm điều đó, đặc biệt là nó có thể được sử dụng như một công việc xung quanh các lớp cũ không được thiết kế tốt trên khái niệm hàm thành viên const.

+0

Bí quyết dastardly là gì! ... Tôi sẽ sử dụng nó :) – Olumide

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