问题
I have a function like (please don't care about returning temporary by reference. This is just an example to explain the problem),
const foo<const int>& get_const()
{
foo<int> f;
return f;
}
This obviously won't compile. I am looking for a way to ensure callers won't change the T
of foo
. How can I ensure that?
I have seen the similar behavior for boost::shared_ptr
. shared_ptr<T>
is convertible to const shared_ptr<const T>
. I couldn't figure out how it is doing this.
Any help would be great.
回答1:
The compiler sees foo<T>
and foo<const T>
as two completely different and unrelated types, so the foo
class needs to support this explicitly just as with any other conversion. If you have control over the foo
class, you need to provide a copy constructor or an implicit conversion operator (or both).
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;
};
回答2:
Assuming that Foo is defined something like this:
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;
};
Then, in order to ensure that clients of Foo do not modify m_value (I assume that this is what is meant by "I am looking for a way to ensure callers won't change the T of foo"), you need to const-qualify the Foo object rather than its template parameter, i.e.
Foo<int> x(1);
x.setValue(2); // OK
const Foo<int> y(1);
y.setValue(2); // does not compile
Therefore, your get_foo function should return a const Foo<T>&
, not a const Foo<const T>&
.
Here's a complete, compilable example:
#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
}
回答3:
If I'm not mistaken, the boost::shared_ptr
implementation has a non-explicit constructor that takes a const T&
reference as an argument and then uses a const_cast
on the RHS's pointer to remove the const
, allowing implicit conversions between them.
Something like this:
shared_ptr(const shared_ptr<const T>& r) : ptr(const_cast<T*>(r.ptr)) {}
Is that what you're looking for?
回答4:
First of all, you're returning a local object by reference...that's not good.
foo and foo are two different types so you'll have to write code (conversion constructors) to explicitly convert them.
To get what you wanted, consider this:
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
If you done your encapsulation correctly and only access members with const getter and non-const setters, this should be good enough for you. Remember if people really want to change your object, they can always const_cast. Const-correctness is only to catch unintentional mistakes.
来源:https://stackoverflow.com/questions/2290460/converting-from-foot-to-const-fooconst-t-c