Преобразование из "foo <T>" в "const foo <const T>" - C ++ - PullRequest
5 голосов
/ 18 февраля 2010

У меня есть такая функция (пожалуйста, не беспокойтесь о возврате временного по ссылке. Это просто пример для объяснения проблемы),

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

Это явно не скомпилируется. Я ищу способ гарантировать, что вызывающие абоненты не изменят T из foo. Как я могу это гарантировать?

Я видел подобное поведение для boost::shared_ptr. shared_ptr<T> конвертируется в const shared_ptr<const T>. Я не мог понять, как это происходит.

Любая помощь будет отличной.

Ответы [ 4 ]

10 голосов
/ 18 февраля 2010

Компилятор видит foo<T> и foo<const T> как два совершенно разных и не связанных типа, поэтому класс foo должен поддерживать это явно, как и при любом другом преобразовании.Если у вас есть контроль над классом foo, вам нужно предоставить конструктор копирования или оператор неявного преобразования (или оба).

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;
};
1 голос
/ 18 февраля 2010

Предполагая, что Foo определено примерно так:

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;
};

Затем, чтобы гарантировать, что клиенты Foo не изменяют m_value (я предполагаю, что это то, что подразумевается под "Я ищу"способ гарантировать, что вызывающие абоненты не изменят T of foo "), вам нужно const-квалифицировать объект Foo, а не его параметр шаблона, т.е.

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

Следовательно, ваша функция get_foo должна возвращать const Foo<T>&, а не const Foo<const T>&.

Вот полный, компилируемый пример:

#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 голосов
/ 18 февраля 2010

Прежде всего, вы возвращаете локальный объект по ссылке ... это нехорошо.

foo и foo - это два разных типа, поэтому вам придется писать код (конструкторы преобразования) для явногопреобразовать их.

Чтобы получить то, что вы хотели, учтите следующее:

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

Если вы правильно выполнили инкапсуляцию и имеете доступ только к членам с постоянными и неконстантными установщиками, этого должно быть достаточно,Помните, что если люди действительно хотят изменить ваш объект, они всегда могут const_cast.Const-правильность только для того, чтобы ловить непреднамеренные ошибки.

0 голосов
/ 18 февраля 2010

Если я не ошибаюсь, реализация boost::shared_ptr имеет неявный конструктор, который принимает в качестве аргумента ссылку const T&, а затем использует const_cast на указателе RHS для удаления const, позволяя неявные преобразования между ними.

Примерно так:

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

Это то, что вы ищете?

...