Re[7]: Как обойтись без const_cast?
От: uzhas Ниоткуда  
Дата: 15.05.12 11:39
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>50/50 -- может оказаться, а может и не оказаться, т.е. есть и ещё

MZ>какое-то решение. А 50% -- это уже хороший шанс.

вообще-то, если исхода два, то это не означает, что они равновероятны
Re[8]: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 15.05.12 11:40
Оценка:
> MZ>50/50 -- может оказаться, а может и не оказаться, т.е. есть и ещё
> MZ>какое-то решение. А 50% -- это уже хороший шанс.
>
> вообще-то, если исхода два, то это не означает, что они равновероятны

Правда ? А я всегда считал, что равновероятны, и попадал в лучший для
меня. Вот я везунчик!
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Как обойтись без const_cast?
От: Кодт Россия  
Дата: 15.05.12 14:54
Оценка: 2 (2)
Здравствуйте, MasterZiv, Вы писали:

MZ>Тут вопрос в другом, -- нафига вообще вызывать эту функцию

MZ>process(), которая неконстантная, через класс, содержащий константную ссылку ?
MZ>Надо эту проблему решать, а не людей в форуме вопросами мучать.

Ну, у меня есть одно подозрение.

Пусть у нас есть некая модель (например, дерево XML DOM) и некий контроллер, владеющий этой моделью.
Наружу модель отдаётся как константная: нечего кому попало в ней ковыряться, это задача контроллера.
А дальше мы снаружи сообщаем контроллеру, что хотим что-то поковырять с его позволения и его же руками.
std::cout << controller.theModel()->fooItem()->barItem()->buzItem()->name(); // можно
controller.theModel()->fooItem()->barItem()->buzItem()->process(); // нельзя
controller.pleaseModify( controller.theModel()->fooItem()->barItem()->buzItem() ); // можно
anotherController.pleaseModify( controller.theModel()->fooItem()->barItem()->buzItem() ); // нужно дать по пальцам


Об этом я и говорил — про права доступа. Здесь нужно было провести границу "свой-чужой" с помощью private и friend, а провели границу "можно-нельзя" с помощью const.
То есть, простой выход — запихать A::process (и, видимо, многое другое) в private, и убрать ставшую ненужной константность.
Перекуём баги на фичи!
Re: Как обойтись без const_cast?
От: Андрей Е  
Дата: 17.05.12 07:48
Оценка:
Спасибо всем кто ответил. Я почитал ответы и понял, что задача сформулирована не совсем полно. Более полная формулировка такая:
Есть код:
class A {
public:
    void process() {}
};

class B {
public:
    B(const A* a)
        : m_a(a)
    {}
    
    const A* getA() const {return m_a;}
private:
    const A* m_a;
};


class C {
public:
    void process() {
        m_b.getA()->process(); // Ошибка! m_b.getA() возвращает const A*, а нужно A*
    }
private:    
    std::vector<A> m_aa;
    B m_b;
};


Есть ТРИ класса. Класс C владеет списком экземпляров класса A и экземпляром класса B, причём экземпляр класса B ссылается на один из экземпляров класса A из списка.
Так как класс C является владельцем, то он имеет право как угодно изменять свои экземпляры классов A и B.
Класс B должен иметь возможность доступаться до константых методов своего объекта класса A, так как ему нужна информация о его состоянии.
Класс C в функции process хочет вызвать неконстантную функцию process именно того объекта класса A на который ссылается его объект класса B.

Возможно в этом случае решение найти проще.
Глядя на эту схему возникает мысль, что возможно проблема состоит в том, что мы пытаемся использовать указатель B::m_a для двух разных целей: для получения информации об A изнутри B и для связи объекта B и A в классе C.
Хотя с другой стороны если хранить ещё один указатель или индекс или итератор, то вроде как возникает дублирование одной и той же информации.
Re[2]: Как обойтись без const_cast?
От: carpenter СССР  
Дата: 17.05.12 08:18
Оценка:
Здравствуйте, Андрей Е, Вы писали:

решение навскидку —

перегрузите getA возвращающую неконстантный указатель,
сделайте эту функцию приватной а класс С другом
Re[2]: Как обойтись без const_cast?
От: BitField Украина http://lazy-bitfield.blogspot.com
Дата: 17.05.12 15:41
Оценка:
Здравствуйте, Андрей Е, Вы писали:


АЕ>class C {

АЕ>public:
АЕ> void process() {
АЕ> m_b.getA()->process(); // Ошибка! m_b.getA() возвращает const A*, а нужно A*
АЕ> }
АЕ>private:
АЕ> std::vector<A> m_aa;
АЕ> B m_b;
АЕ>};
АЕ>[/ccode]

Из серии "как только люди не извращаются, когда кодинг-стиль кривой":

Есть еще один трюк с черной магией. Поскольку m_b.getA() гарантированно указывает на один из обьектов в m_aa, делаем так:
A * nonConst = &m_aa[0] + (m_b.getA() - m_aa[0]);

Не забыть комментарий, что мы тут делаем, и ассерт.

Скорее всего, компилятор это соптимизирует.
Re[2]: Как обойтись без const_cast?
От: Кодт Россия  
Дата: 17.05.12 18:29
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>
АЕ>class A {
АЕ>private:
АЕ>  void process() {}
     friend class C;
АЕ>};
АЕ>

И сделать A* неконстантным в классе B — больше там константность не нужна.



А если политик доступа много (т.е. реально у тебя не три класса), то, возможно, придётся или как-то переразбивать классы, или изгаляться, или просто забить в желании переложить ответственность за прямоту своих рук на компилятор. Быть внимательным, короче говоря

Пример переразбивания
class AforC
{
protected: // потому что для класса A это тоже можно
  void process(); // только для класса C, и больше ни для кого
  friend class C;
};
class AforD
{
protected:
  void perform();
  friend class D;
};
class A: public AforC, public AforD {};

Если компоненты должны знать друг о друге и о композитном классе — то либо forward declaration и вынести все определения за объявление класса A,
class AforC
{
protected:
  friend class A;
  friend class AforD;
  friend class C;
  void process();
};

class AforD
{
protected:
  friend class A;
  friend class D;
  void perform();
};

class A: public AforC, public AforD
{
public:
  void execute() { process(); perform(); } // можно инлайнить, ибо объявления выше
};

// а эти определения вынесены из объявления классов
inline void AforC::process() { static_cast<A*>(this)->perform(); }
inline void AforD::perform() { static_cast<A*>(this)->execute(); }

Либо — то же самое, но с помощью CRTP
template<class A> class AforC
{
protected:
  friend class AforD;
  friend class C;
  void process() { static_cast<A*>(this)->perform(); }
};

template<class A> class AforD
{
protected:
  friend class A;
  friend class D;
  void perform() { static_cast<A*>(this)->execute(); }
};

class A: public AforC<A>, public AforD<A>
{
public:
  void execute() { process(); perform(); } // можно инлайнить, ибо объявления выше
};
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.