Преобразование из “нечто <T>” к “нечто константы <константа T>” - C++

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

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

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

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

Любая справка была бы большой.

5
задан Peter Alexander 18 February 2010 в 17:07
поделиться

4 ответа

Компилятор видит foo и foo как два совершенно разных и несвязанных типа, поэтому класс 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;
};
10
ответ дан 13 December 2019 в 19:26
поделиться

Предполагая, что 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»), вам нужно константно квалифицировать объект Foo, а не его параметр шаблона, т.е.

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

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

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

#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
}
1
ответ дан 13 December 2019 в 19:26
поделиться

При сравнении языков важно помнить о том, что если вы делаете простой пошаговый перевод, вы не сравниваете яблоки с яблоками.

Что имеет смысл в одном языке может иметь ужасные побочные эффекты в другом. Чтобы действительно сравнить характеристики производительности, вам нужна версия C # и C++, и код для этих версий может быть очень разным. Например, в C # я бы даже не использовал одну и ту же сигнатуру функции. Я бы пошел с чем-то более похожим:

IEnumerable<int> Fibonacci()
{
   int n1 = 0;
   int n2 = 1;

   yield return 1;
   while (true)
   {
      int n = n1 + n2;
      n1 = n2;
      n2 = n;
      yield return n;
   }
}

и затем обернуть это так:

public static int fib(int n)
{
    return Fibonacci().Skip(n).First();
}

Это будет гораздо лучше, потому что он работает снизу вверх, чтобы воспользоваться расчетами в последнем сроке, чтобы помочь построить следующий, а не два отдельных набора рекурсивных вызовов.

И если вы действительно хотите кричать производительность в C++ вы можете использовать мета-программирование, чтобы сделать компилятор предварительно вычислить ваши результаты следующим образом:

template<int N> struct fibonacci
{
    static const int value = fibonacci<N - 1>::value + fibonacci<N - 2>::value;
};

template<> struct fibonacci<1>
{
    static const int value = 1;
};

template<> struct fibonacci<0>
{
    static const int value = 0;
};
-121--2776716-

Также если вы просматриваете файловую систему с помощью обозревателя файлов netrw, вы можете установить текущий каталог, нажав клавишу c.

-121--746345-

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

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-getter и non-const, это должно быть достаточно хорошо для вас. Помните, если люди действительно хотят изменить ваш объект, они всегда могут const_cast. Конст-корректность - это только уловить непреднамеренные ошибки.

0
ответ дан 13 December 2019 в 19:26
поделиться

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

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

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

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

0
ответ дан 13 December 2019 в 19:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: