Как иметь дело с инициализацией ссылочного участника неконстанты в объекте константы?

Вы были бы в состоянии использовать USB-> адаптер RS232? У меня есть некоторые, и они просто используют драйвер FTDI. Затем необходимо быть в состоянии переименовать/dev/ttyUSB0 (или независимо от того, что создается) как/dev/ttyS2.

6
задан Catskul 4 August 2011 в 08:17
поделиться

6 ответов

Ваш пример не терпит неудачу, k передан по стоимости. Элемент i является «неявно постоянным», поскольку прямые члены C не могут быть изменены, когда экземпляр является постоянным.
Констанс говорит, что вы не можете изменять члены после инициализации, но инициализация их значениями в списке инициализации, конечно, разрешена - как еще вы могли бы дать им значение?

Что не работает, так это вызов конструктора без его выполнения публично;)

update адрес обновленный вопрос:

Да, C ++ иногда вынуждает вас к некоторой многословности, но const корректность - это обычное стандартное поведение, которое вы не можете просто переопределить, не нарушив ожиданий. Ответ Павла уже объясняет одну общую идиому, которая используется в проверенных библиотеках, таких как STL, для обхода этой ситуации.

Иногда нужно просто согласиться с тем, что у языков есть ограничения, и при этом учитывать ожидания пользователей интерфейса,

5
ответ дан 8 December 2019 в 13:46
поделиться

Похоже, вам нужен объект, который может заключать либо int * (и затем вести себя как неконстантные), либо int const * (а затем вести себя как const). Вы не можете сделать это правильно с одним классом.

Фактически, само понятие, что const , примененное к вашему классу, должно изменить его семантику, как это неверно - если ваш класс моделирует указатель или итератор (если он оборачивает указатель, скорее всего, так и есть), то применение к нему const должно означать только то, что он не может быть изменен сам по себе, и не должен подразумевать что-либо относительно указанного значения. Вам следует подумать о том, что STL делает для своих контейнеров - именно поэтому он имеет разные классы итератора и const_iterator , причем оба являются разными, но первое неявно может быть преобразовано во второе. Кроме того, в STL const итератор не то же самое, что const_iterator ! Так что сделайте то же самое.

[РЕДАКТИРОВАТЬ] Вот хитрый способ максимального повторного использования кода между C и const_C , обеспечивая при этом постоянную корректность и не углубляясь в UB (с const_cast ):

template<class T, bool IsConst>
struct pointer_to_maybe_const;

template<class T>
struct pointer_to_maybe_const<T, true> { typedef const T* type; };

template<class T>
struct pointer_to_maybe_const<T, false> { typedef T* type; };

template<bool IsConst>
struct C_fields {
   typename pointer_to_maybe_const<int, IsConst>::type i;
   // repeat for all fields
};


template<class Derived>
class const_C_base {
public:
    int method() const { // non-mutating method example
        return *self().i;
    }
private:
    const Derived& self() const { return *static_cast<const Derived*>(this); }
};

template<class Derived>
class C_base : public const_C_base<Derived> {
public:
    int method() { // mutating method example
        return ++*self().i;
    }
private:
    Derived& self() { return *static_cast<Derived*>(this); }
};


class const_C : public const_C_base<const_C>, private C_fields<true> {
    friend class const_C_base<const_C>;
};

class C : public C_base<C>, private C_fields<false> {
    friend class C_base<C>;
};

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

12
ответ дан 8 December 2019 в 13:46
поделиться

Ваш вопрос не имеет смысла. Откуда вы взяли все эти предсказания «это не удастся»? Ни один из них не является даже отдаленно верным.

Во-первых, совершенно неважно, объявлен ли параметр конструктора const или нет. Когда вы передаете по значению (как в вашем случае), вы можете передать объект const в качестве аргумента в любом случае, независимо от того, объявлен ли параметр как const или нет.

Во-вторых, с точки зрения конструктора, объект является константой НЕ . Независимо от того, какой объект вы строите (постоянный или нет), внутри конструктора объект никогда константа. Так что нет необходимости в mutable или что-то в этом роде.

Почему бы вам просто не попробовать скомпилировать код (чтобы убедиться, что ничего не выйдет из строя) вместо того, чтобы делать странные необоснованные прогнозы, что что-то «выйдет из строя»?

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

Когда вы создаете экземпляр

const C c1(...)

, поскольку c1 является константой, его член i превращается в:

int* const i;

Как уже упоминалось, это называется неявной константой.

Теперь, позже в вашем примере, вы пытаетесь передать const int *. Итак, ваш конструктор в основном делает следующее:

const int* whatever = ...;
int* const i = whatever; // error

Причина, по которой вы получаете ошибку, в том, что вы не можете преобразовать константу в неконстантную. Указатель "любой" не может изменять то, на что он указывает (часть int - const). Указателю «i» разрешено изменять то, на что он указывает, но он не может быть изменен сам по себе (часть указателя - const).

Вы также упомянули, что хотите, чтобы ваш класс моделировал указатель. STL делает это с помощью итераторов. Модель, которую используют некоторые реализации, имеет класс под названием «const_iterator», который скрывает реальный указатель и предоставляет только константные методы для доступа к указанным данным. Также существует класс «итератор», который наследуется от «const_iterator», добавляя неконстантные перегрузки. Это прекрасно работает - это настраиваемый класс, который допускает ту же константу, что и указатели, где типы отражают указатели следующим образом:

  • итератор -> T *
  • const итератор -> T * const
  • const_iterator -> const T *
  • const const_iterator -> const T * const

Надеюсь, это имеет смысл :)

Также существует класс «итератор», который наследуется от «const_iterator», добавляя неконстантные перегрузки. Это прекрасно работает - это настраиваемый класс, который допускает ту же константу, что и указатели, где типы отражают указатели следующим образом:

  • итератор -> T *
  • const итератор -> T * const
  • const_iterator -> const T *
  • const const_iterator -> const T * const

Надеюсь, это имеет смысл :)

Также существует класс «итератор», который наследуется от «const_iterator», добавляя неконстантные перегрузки. Это прекрасно работает - это настраиваемый класс, который допускает ту же константу, что и указатели, где типы отражают указатели следующим образом:

  • iterator -> T *
  • const iterator -> T * const
  • const_iterator -> const T *
  • const const_iterator -> const T * const

Надеюсь, это имеет смысл :)

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

const int * не то же самое, что int * const. Когда ваш класс const, у вас есть последний (постоянный указатель на изменяемое целое число). То, что вы передаете, является первым (изменяемый указатель на постоянное целое число). По очевидным причинам они не являются взаимозаменяемыми.

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

Хорошо, вот что я сделал до сих пор. Чтобы разрешить наследование после константной версии класса без const_casts или дополнительных служебных данных, я создал объединение, которое в основном выглядит как ths:

template <typename T>
union MutatedPtr
{
protected:
    const T * const_ptr;
    T * ptr;

public:
    /**
     * Conversion constructor.
     * @param ptr pointer.
     */
    MutatedPtr(const T * ptr): const_ptr(ptr) {};

    /**
     * Conversion to T *.
     */
    operator T *() {return ptr;}

    /**
     * Conversion to const T *.
     */
    operator const T *() const {return const_ptr;}
};

Когда объявляется поле MutatedPtr, оно заканчивается так, что в константных методах возвращается const_ptr, а неконстантные. получить простой ptr. Он делегирует константу метода цели указателя, что имеет смысл в моем случае.

Есть комментарии?

Кстати, вы, конечно, можете сделать то же самое с не-указательными типами или даже методами, так что похоже, что введение изменяемое ключевое слово не нужно (?)

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

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