Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Сентябрь 21, 2012, 11:30



Название: Константность
Отправлено: Igors от Сентябрь 21, 2012, 11:30
Добрый день

Слышал что const_cast - это плохо, очень плохо, явный дефект в архитектуре и.т.п. Всем понятно что чем меньше приведений типов - тем лучше, однако иногда я не вижу способа их избежать. Вот напр такая ситуация

Код
C++ (Qt)
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
 
Само тело FindItem вызывает только др константные методы, все гуд. А вот с возвращенным указателем (на данные класса) сложнее. В большинстве случаев он const, но иногда необходимо менять данные по этому указателю. Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.

Как Вы решаете такую ситуацию?

Спасибо


Название: Re: Константность
Отправлено: QtCoder от Сентябрь 21, 2012, 11:46
Раз иногда надо менять, то значит он никакой не const.
Тогда просто MyItem * FindItem() const;


Название: Re: Константность
Отправлено: Igors от Сентябрь 21, 2012, 12:10
Раз иногда надо менять, то значит он никакой не const.
Тогда просто MyItem * FindItem() const;
Так не могу вернуть - MyItem указывает на данные класса


Название: Re: Константность
Отправлено: Akon от Сентябрь 21, 2012, 12:46
Igors:
То, что вы привели - есть const propagation (распространиение константности), когда имея конст. ссылку мы можем получить только конст. на другие объекты. В противоположность этому
Код:
MyItem * FindItem() const;
прерывает константность, т.е. имея конст. ссылку мы можем получить неконст. ссылку на другие объекты. Очевидно, const propagation делает код более строгим.

Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.


Название: Re: Константность
Отправлено: Igors от Сентябрь 21, 2012, 12:51
Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.
Так понял что const_cast здесь неизбежно. Согласен с этим. А с volatille mutable не лучше выходит? (не пробовал)


Название: Re: Константность
Отправлено: LisandreL от Сентябрь 21, 2012, 14:54
Слышал что const_cast - это плохо, очень плохо, явный дефект в архитектуре и.т.п.
Я писал «почти наверняка», так что не совсем «явный», некоторую долю сомнения я оставил.  :D

Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.
Соглашусь. Повторять 2 раза плохо прежде всего из-за того, что потом меняя код можно в одном месте можно забыть изменить его в другом.

Как Вы решаете такую ситуацию?
Зависит от ситуации. Где возможно делаю геттеры и сеттеры. Но не при любом MyItem такой подход удобен/применим.

Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.
Ну да, как-то так получается. В своём коде и чётко понимая что делаешь.
Хуже ситуация, когда нечто такое было бы удобно с чужим кодом/библиотекой.
Мы ведь умные, можем посмотреть, как там всё устроено и что снятием константности мы ничего не сломаем.
А потом берём следующую версию библиотеки и программный интерфейс там тот же, а код - другой, и наш «хак» приводит к самым непредвиденным последствиям.
И в принципе тоже возможно и с собственным кодом при изменениях, если забудем, что где-то снимали константность.
В общем я придерживаюсь правил при использовании const_cast: со сторонним кодом не использовать, в полностью своём кодом  обходиться как с сильнодействующим лекарством: применять если «ожидаемая польза превышает потенциальный риск». Не претендую на истинность в последней инстанции.

P.S.
Недавно использовал Speex.
Кодирование фрейма:
Код
C++ (Qt)
int speex_encode_int (void *state, spx_int16_t *in, SpeexBits *bits)
Входной буффер неконстаным указателем?! Вроде бы по логике не должен он изменяться внутри, но вдруг… И что мне локальную копию делать? Хм…

Что интересно в API предлагаемого теперь на замену Opus'а такого огреха нет:
Код
C++ (Qt)
opus_int32 opus_encode(OpusEncoder *st,  const opus_int16 *pcm, int frame_size,  unsigned char *data, opus_int32 max_data_bytes)


Название: Re: Константность
Отправлено: DmitryM от Сентябрь 21, 2012, 15:51
Слышал что const_cast - это плохо, очень плохо, явный дефект в архитектуре и.т.п. Всем понятно что чем меньше приведений типов - тем лучше, однако иногда я не вижу способа их избежать. Вот напр такая ситуация

Код
C++ (Qt)
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
 
Само тело FindItem вызывает только др константные методы, все гуд. А вот с возвращенным указателем (на данные класса) сложнее. В большинстве случаев он const, но иногда необходимо менять данные по этому указателю. Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.

Спасибо
Менять содержимое объекта класса MyItem, не оповещая объект класса MyClass,  не хорошо с точки зрения ООП.


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 21, 2012, 18:39
Igors:
То, что вы привели - есть const propagation (распространиение константности), когда имея конст. ссылку мы можем получить только конст. на другие объекты. В противоположность этому
Код:
MyItem * FindItem() const;
прерывает константность, т.е. имея конст. ссылку мы можем получить неконст. ссылку на другие объекты. Очевидно, const propagation делает код более строгим.
Вообще там такого метода нет.
В примера константный метод возвращает константный указатель, а неконстантный соответственно неконст.
Так что все по-честному.

А по теме. Я думаю, приведенный пример это не очень удачное проектирование.
Сама по себе операция поиск она константная (чисто логически), поэтому метод findItem() должен быть только const.
И видимо должен возвращать что-то типа индекса, для доступа к данным в этом классе.
А уже имея индекс мы должны иметь возможность получить оба варианта указателя.
Второй момент вызывая неконстантный findItem я не могу быть уверен(если не я писал этот класс конечно), что объект сохранится неизменным, даже если я не стану менять полученный элемент.
Может там есть какой-нибудь счетчик доступа или еще что-нибудь, какие-нибудь механизмы включаются.

А с volatille не лучше выходит? (не пробовал)
А с volatile мне кажется не получится либо получится полная ерунда (это если volatile ставить на сами данные).
Что можно попробовать это сохранять внутри класса этот самый индекс, о котором я выше писал.
А уже из самих функций findItem вызывать соответствующий метод getItem или getConstItem получая нужный указатель.
Но чем такие игры лучше чем, const_cast я не вижу.


Название: Re: Константность
Отправлено: Igors от Сентябрь 21, 2012, 19:00
Не то чтобы эта ситуация очень частая, но и какой-то редкой/экзотической ее не назовешь. Возвращение указателя на константу нужно по понятным причинам, напр для пользования классом извне. С др стороны вот я делаю какую-то содержательную работу - возможно методом того же MyClass. Да, изменяю объект - не скрываю, так что, мне FindItem не использовать? Приходится рисовать как-то так
Код
C++ (Qt)
MyItem * MyClass::FindItem( ... )
{
const MyItem * itm = FindItem(..);
return const_cast <MyItem *> (itm);
}
 
Есть лучшие предложения?


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 21, 2012, 19:03
То что я описал про volatile примерно так. Может как-то украсить можно, убрать дублирование. Но я лишь идею с volatile показать хотел.

Код
C++ (Qt)
struct Data
{
   int a;
   int b;
};
 
class Test
{
public:
   Data * findA(int A)
   {
       int index = findAImpl(A);
       return getA(index);
   }
 
   const Data * findA(int A) const
   {
       int index = findAImpl(A);
       return getA(index);
   }
 
   Data *getA(int index)
   {
       if(index > 0 && index < data.length())
           return data[index];
       else
           return 0;
   }
 
   const Data *getA(int index) const
   {
       if(index > 0 && index < data.length())
           return data[index];
       else
           return 0;
   }
 
private:
   QList<Data *>data;
 
   int findAImpl(int A) const
   {
       for (int i = 0; i < data.size(); ++i)
           if(data[i]->a == A)
               return i;
       return -1;
   }
 
   volatile int index_;
};


Название: Re: Константность
Отправлено: DmitryM от Сентябрь 21, 2012, 19:58
Если бы я писал библиотеку и столкнулся бы с такой ситуацией, то возвращал бы не ссылку на объект, а прокси объект.
Никого же не удивляет стандартный begin()
Код
C++ (Qt)
iterator begin ();
const_iterator begin () const;
 


Название: Re: Константность
Отправлено: Igors от Сентябрь 21, 2012, 20:15
Но я лишь идею с volatile показать хотел.

Код
C++ (Qt)
struct Data
..
   volatile int index_;
};
Цитировать
- Поручик, но зачем же они яйца закапывают?
- Дикари-с
:) Чего ж Вы volatile к индексу прилепили? Здесь он никакого отношения к константности не имеет.
Вариант с индексом понятен, но требует ощутимых переделок. Напр я вставляю новые элементы в тот же QList, адрес элемента у меня неизменный, а вот индекс "уплывает". Индекс может быть сложен (напр для дерева) да и не уверен в большом счастье обладания индексом - да, компилятору рот закрыли, но теперь самому решать когда же он валиден.

Если бы я писал библиотеку и столкнулся бы с такой ситуацией, то возвращал бы не ссылку на объект, а прокси объект.
Никого же не удивляет стандартный begin()
Код
C++ (Qt)
iterator begin ();
const_iterator begin () const;
 
Называется "сменял шило на мыло", теперь какой итератор вернуть - константный или нет


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 21, 2012, 23:09
Чего ж Вы volatile к индексу прилепили? Здесь он никакого отношения к константности не имеет.
Да. поторопился. Я хотел сохранять найденный индекс в index_, поэтому поставил volatile.
Но это даже не пригодилось.


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 21, 2012, 23:43
Вся соль в том, чтобы отделить мухи от котлет: сам механизм поиска (сложный метод, который должен быть константным) и возврат указывающей сущности (указель, ссылка или итератор) - это пару строк, тут видимо будет дублирование кода (как в моем примере).
И я не понял насчет уплывания индекса. Ну и пусть уплывает.
Этот индекс предназначен для внутреннего использования и актуален только на момент поиска.
Можно методы getA сделать приватными.
Пользователь класса все равно в результате получает искомый указатель "требуемой константности" :)

Боже. Вот меня заглючило. Говоря volatile я имел в виду mutable. :)


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 22, 2012, 00:08
А вот вообще без заморочек:
Код
C++ (Qt)
class Test
{
public:
   Data * findA(int A)
   {
       return findAImpl(A);
   }
 
   const Data * findA(int A) const
   {
       return findAImpl(A);
   }
 
private:
   QList<Data *>data;
 
   Data * findAImpl(int A) const
   {
       for (int i = 0; i < data.size(); ++i)
           if(data[i]->a == A)
               return data[i];
       return 0;
   }
};
;)


Название: Re: Константность
Отправлено: DmitryM от Сентябрь 22, 2012, 09:33
Называется "сменял шило на мыло", теперь какой итератор вернуть - константный или нет
Учи мат. часть. Возвращаемое значение функции, не является частью сигнатурой функции,  поэтому так писать не правильно
Код
C++ (Qt)
   Data * findA(int A)
   {
       int index = findAImpl(A);
       return getA(index);
   }
 
   const Data * findA(int A) const
   {
       int index = findAImpl(A);
       return getA(index);
   }
 

Однако шаблоны предоставляют выбор, какое значение получать
Код
C++ (Qt)
#include <vector>
 
class Test
{
public:
   Test()
   {
       vec_.push_back(1);
       vec_.push_back(2);
 
       foo();
       bar();
   }
 
   void foo()
   {
      std::vector<int>::iterator it = vec_.begin();
      std::vector<int>::const_iterator cit = vec_.begin();
   }
 
   void bar()const
   {
      std::vector<int>::iterator it = vec_.begin(); //  error: conversion from 'std::vector<int>::const_iterator' to non-scalar type std::vector<int>::iterator' requested
      std::vector<int>::const_iterator cit = vec_.begin();
   }
private:
   std::vector<int> vec_;  
};
 
int main()
{
   Test a();
   return 0;
}
 


Название: Re: Константность
Отправлено: Igors от Сентябрь 22, 2012, 11:32
Боже. Вот меня заглючило. Говоря volatile я имел в виду mutable. :)
Я тоже перепутал в посте #4  :) Исправил. Вроде mutable и предназначен для таких целей, но я не знаю как "вернуть mutable указатель". А завести mutable член, перелить сначала в него и затем вернуть можно, но не яснее и даже не короче


Название: Re: Константность
Отправлено: DmitryM от Сентябрь 22, 2012, 20:39
:рукалицо:
Igors, andrew.k если вы себя называете C++ программистами, то ответьте на вопрос, вызов какого FindItem должен поставить компилятор?
Код:
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
int mail(){
   MyClass foo();
   //....
   foo. FindItem();
}


Название: Re: Константность
Отправлено: andrew.k от Сентябрь 23, 2012, 01:54
:рукалицо:
Igors, andrew.k если вы себя называете C++ программистами, то ответьте на вопрос, вызов какого FindItem должен поставить компилятор?
Код:
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
int mail(){
   MyClass foo();
   //....
   foo. FindItem();
}
Тут будет вызов неконстантной версии, т.к. функция mail?? (ты я так понимаю, нас не считаешь за программстов, но себя да : ) не имеет спецификатора const.

Но к чему этот вопрос? Рукалицо это ты удачно подметил.

Насчет первого примера, я торопился написал ерунду, не проверил даже. Я написал это выше.
Есть претензии ко второму примеру?


Название: Re: Константность
Отправлено: Igors от Сентябрь 23, 2012, 06:02
Насчет первого примера, я торопился написал ерунду, не проверил даже. Я написал это выше.
Есть претензии ко второму примеру?
Если искать решение самому, то ошибки неизбежны, и это нормально. А вот отвечать, на мой взгляд, не всякому следует. "если вы считаете себя", "учи матчасть" - борзости с избытком. А доходит до дела - тупо лепит книжку и даже не в тему. Ну и зачем вступать с ним в бесполезные пререкания?