2010-02-18 30 views
5

C++ Tôi có một chức năng tương tự (. Xin đừng quan tâm đến việc trở lại tạm thời bằng cách tham khảo Đây chỉ là một ví dụ để giải thích vấn đề),Chuyển đổi từ "foo <T>" thành "foo const <const T>" -

const foo<const int>& get_const() 
{ 
    foo<int> f; 
    return f; 
} 

Điều này rõ ràng sẽ không biên dịch. Tôi đang tìm cách đảm bảo người gọi sẽ không thay đổi số T của foo. Làm thế nào tôi có thể đảm bảo điều đó?

Tôi đã thấy hành vi tương tự cho boost::shared_ptr. shared_ptr<T> có thể chuyển đổi thành const shared_ptr<const T>. Tôi không thể hiểu được nó đang làm thế nào.

Mọi trợ giúp đều tuyệt vời.

+0

Có thể bạn đang cố gắng đảm bảo rằng người gọi sẽ không thay đổi * f * của foo. –

Trả lời

0

Nếu tôi không nhầm, việc thực hiện boost::shared_ptr có một constructor không rõ ràng mà phải mất một tài liệu tham khảo const T& như một cuộc tranh cãi và sau đó sử dụng một const_cast trên con trỏ của RHS để loại bỏ các const, cho phép chuyển đổi ngầm giữa chúng.

Something như thế này:

shared_ptr(const shared_ptr<const T>& r) : ptr(const_cast<T*>(r.ptr)) {} 

Là những gì bạn đang tìm kiếm?

+0

'const_cast' == Cá trích đỏ cho tôi :) OP đang tìm kiếm điều gì đó ngược lại trong thực tế (điều này tự nhiên hơn nhiều). –

1

Giả sử rằng Foo được định nghĩa như thế này:

template<typename T> class Foo 
{ 
public: 
    Foo(const T& value) : m_value(value) { } 
    const T& getValue() const { return m_value; } 
    void setValue(const T& value) { m_value = value; } 
private: 
    T m_value; 
}; 

Sau đó, để đảm bảo rằng khách hàng của Foo không sửa đổi m_value (Tôi cho rằng đây là những gì có nghĩa là "Tôi đang tìm kiếm một cách để đảm bảo những người gọi sẽ không thay đổi T của foo "), bạn cần phải const đủ điều kiện đối tượng foo chứ không phải là tham số mẫu của nó, tức là

Foo<int> x(1); 
x.setValue(2); // OK 
const Foo<int> y(1); 
y.setValue(2); // does not compile 

Do đó, chức năng get_foo bạn nên trả về một const Foo<T>&, không phải là một const Foo<const T>&.

Dưới đây là một hoàn thành, compilable dụ:

#include <iostream> 

template<typename T> class Foo 
{ 
public: 
    Foo(const T& value) : m_value(value) { } 
    const T& getValue() const { return m_value; } 
    void setValue(const T& value) { m_value = value; } 
private: 
    T m_value; 
}; 

template<class T> class Owner 
{ 
public: 
    Owner(const T& value) : m_foo(value) { } 
    Foo<T>& getFoo() { return m_foo; } 
    const Foo<T>& getConstFoo() const { return m_foo; } 

private: 
    Foo<T> m_foo; 
}; 


int main(int argc, char** argv) 
{ 
    Owner<int> x(1); 
    x.getFoo().setValue(2); 
    // x.getConstFoo().setValue(3); // will not compile 
} 
+0

Trong khi đây là một câu trả lời tuyệt vời, có những trường hợp khi 'const foo ' thực sự là những gì cần thiết, đặc biệt nếu 'T' là một kiểu con trỏ hoặc nếu foo lưu trữ' T * '. –

+0

@Tyler Đồng ý, OP có phần không rõ ràng - trong khi nó yêu cầu làm thế nào để chuyển đổi Foo thành Foo , nó cũng nói "Tôi đang tìm kiếm một cách để đảm bảo người gọi sẽ không thay đổi T của foo", đó là những gì câu trả lời của tôi nhằm mục đích đạt được bằng cách const-vòng loại đối tượng Foo chính nó. –

+0

Cảm ơn câu trả lời. Tôi đang tìm kiếm những gì @Tyler đã đề cập. –

9

Trình biên dịch thấy foo<T>foo<const T> như hai loại hoàn toàn khác nhau và không liên quan, vì vậy lớp foo cần hỗ trợ này một cách rõ ràng cũng giống như với bất kỳ chuyển đổi khác. Nếu bạn có quyền kiểm soát lớp foo, bạn cần cung cấp một hàm tạo bản sao hoặc một toán tử chuyển đổi ngầm (hoặc cả hai).

template<typename T> 
class foo 
{ 
public: 

    // Regular constructor 
    foo(T t) : t(t) {} 

    // Copy constructor (works for any type S convertable to T, in particular S = non-const T if T is const) 
    // Remember that foo<T> and foo<S> are unrelated, so the accessor method must be used here 
    template<typename S> foo (const foo<S>& copy) : t(copy.getT()) {} 

    // Accessor 
    T getT() const { return t; } 

    // Conversion operator 
    operator foo<const T>() const { return foo<const T>(t); } 

private: 

    T t; 
}; 
+0

Tuyệt vời. Cảm ơn rất nhiều. –

0

Trước hết, bạn trả về đối tượng địa phương bằng cách tham chiếu ... điều đó không tốt.

foo và foo là hai loại khác nhau, do đó bạn sẽ phải viết mã (trình tạo chuyển đổi) để chuyển đổi chúng một cách rõ ràng.

Để có được những gì bạn muốn, hãy xem xét điều này:

template <typename T> 
struct foo {T* t;}; 

const foo<int>& get_const(const foo<int>& f) { 
    return f; 
} 

foo<int> f; 
const foo<int>& cf = get_const(f); 
f.t = 0; // ok, f is not const 
*cf.t = 0; // ok because cf.t is const but what cf.t points to is not 
cf.t = 0; // compiler error cf.t is const and cannot be lvalue 

foo<int>& cf = get_const(f); // compiler error, cannot convert non-const to const without const_cast 

Nếu bạn thực hiện đóng gói của bạn một cách chính xác và chỉ các thành viên truy cập với getter const và setters không const, điều này sẽ được tốt đủ cho bạn. Hãy nhớ nếu mọi người thực sự muốn thay đổi đối tượng của bạn, họ luôn có thể const_cast. Const-chính xác là chỉ để nắm bắt những sai lầm không chủ ý.

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