Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting from "foo<T>" to "const foo<const T>" - C++

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.

like image 593
Navaneeth K N Avatar asked Feb 18 '10 16:02

Navaneeth K N


2 Answers

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;
};
like image 58
Tyler McHenry Avatar answered Nov 13 '22 02:11

Tyler McHenry


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
}
like image 1
Gareth Stockwell Avatar answered Nov 13 '22 01:11

Gareth Stockwell