Как обойтись без const_cast?
От: Андрей Е  
Дата: 14.05.12 07:55
Оценка:
Есть такой код:
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;
};

void f(const std::vector<A*>& aa, const B& b) {
    // Известно, что указатель хранящийся в b так же содержиться и в aa.
    // Нужно сделать так:
    b.getA()->process(); // Ошибка! b.getA() возвращает const A*, а нужно A*
}


Подскажите пожалуйста какой лучший способ снять константность с указателя на A в функции f?
Можно сделать const_cast — но это запрещено в code style.
Снимать константность с B::m_a не хочется, так как это повлечёт за собой снятие константности в куче других мест, как в приведённом фрагменте, так и за его пределами в реальной программе из которой этот фрагмент выделен. Тем более, что сам класс B никак не меняет экземпляр на который указывает m_a.

На текущий момент, я додумался только до того, чтобы искать в aa такое же значение, что и в b и использовать его.

Может быть в данной ситуации есть какое-то более простое, быстрое и красивое решение? Если вы его знаете, подскажите пожалуйста!

Может быть нужно слегка поменять структуру этого фрагмента? Мне нужно, чтобы экземпляр класса B мог ссылаться на конкретный экземпляр класса A и вызывать его константные функции. При этом экземпляр класса A реально храниться где-то в другом месте. Как именно организован этот механизм(через указатели или итераторы или что-то ещё) не принципиально.
const_cast
Re: Как обойтись без const_cast?
От: MTD https://github.com/mtrempoltsev
Дата: 14.05.12 08:01
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Подскажите пожалуйста какой лучший способ снять константность с указателя на A в функции f?


process изменяет состояние класса? Тогда у меня плохие новости — надо пересматривать дизайн, иначе не вижу проблемы сделать process константной, все остальное игры с UB.
Re: Как обойтись без const_cast?
От: uzhas Ниоткуда  
Дата: 14.05.12 08:09
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Может быть нужно слегка поменять структуру этого фрагмента?

задача мутная, но попробую предложить такое (метакод):
class A
{
public:
  int GetID() const;
  void process() {}
};

class B
{
public:
  B(const A* a)
    : m_a(a)
  {}

  int GetA_ID() const
  {
    return m_a->GetID();
  }

private:
  const A* m_a;
};

void f(const std::vector<A*>& aa, const B& b)
{
  aa.findByID(b.GetA_ID())->process();
}
Re[2]: Как обойтись без const_cast?
От: Андрей Е  
Дата: 14.05.12 10:14
Оценка:
Здравствуйте, uzhas, Вы писали:

U>задача мутная, но попробую предложить такое (метакод):


В вашем варианте есть поиск по массиву, что может занять много времени. Хотелось бы, чтобы задача решалась за константное время.
И к тому же в данном случае, как мне кажется, id ничем не отличается от указателя. Указатель так же является уникальным для каждого объекта и занимает сравнительно немного места в памяти. Зато для него не нужно добавлять никаких функций в классы.
Re: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 14.05.12 10:33
Оценка: 1 (1)
On 05/14/2012 11:55 AM, Андрей Е wrote:


> Может быть в данной ситуации есть какое-то более простое, быстрое и красивое

> решение? Если вы его знаете, подскажите пожалуйста!

class B {
public:
B(A* a)
: m_a(a)
{}

const A* getA()const {return m_a;}
void processA() { m_a->process(); }
private:
A* m_a;
};

void f( ... , B &b )
{
b.processA();
}
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Как обойтись без const_cast?
От: uzhas Ниоткуда  
Дата: 14.05.12 10:42
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>В вашем варианте есть поиск по массиву, что может занять много времени. Хотелось бы, чтобы задача решалась за константное время.

тогда так: http://ideone.com/O7AWM

АЕ>Может быть нужно слегка поменять структуру этого фрагмента?

да. вам нужно убрать const там, где он мешается, чтобы реализовать быстрый алгоритм. либо надо пожертвовать слегка производительностью, добавив доп. уровень косвенности
Re: Как обойтись без const_cast?
От: Sir-G  
Дата: 14.05.12 11:17
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Может быть в данной ситуации есть какое-то более простое, быстрое и красивое решение? Если вы его знаете, подскажите пожалуйста!

Встречался с такими ситуациями. Мне кажется нормальным сделать const_cast, и написать комментарий рядом, что это для производительности. Типа неконстантый доступ есть, но долго искать объект. Можно еще отладочную проверку сделать, что указатель действительно в массиве есть.
Если const_cast жестко запрещен, то придется по-честному в массиве искать.
Re[2]: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 14.05.12 11:24
Оценка:
On 05/14/2012 03:17 PM, Sir-G wrote:

> Встречался с такими ситуациями. Мне кажется нормальным сделать const_cast, и

> написать комментарий рядом, что это для производительности.

Но это же враньё, это не для производительности, а по разгильдяйству.
Зачем врать в комментах?
Posted via RSDN NNTP Server 2.1 beta
Re: Как обойтись без const_cast?
От: Кодт Россия  
Дата: 14.05.12 16:42
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Есть такой код:

АЕ>
АЕ>void f(const std::vector<A*>& aa, const B& b) {
АЕ>    // Известно, что указатель хранящийся в b так же содержиться и в aa.
АЕ>    // Нужно сделать так:
АЕ>    b.getA()->process(); // Ошибка! b.getA() возвращает const A*, а нужно A*
АЕ>}
АЕ>


Если известно априори, то зачем передавать вектор в функцию? (Правда, если это действительно априорная информация, то значит, что в программе слишком много const расставлено не по делу, либо наоборот, где-то их не хватает).

А если по этому вектору как-то пробегают — то заодно можно обнаружить в нём aa[i]==b.getA(), и вызвать aa[i]->process(). Или убедиться, что этого элемента там нет и выругаться.
Перекуём баги на фичи!
Re[4]: Как обойтись без const_cast?
От: Андрей Е  
Дата: 15.05.12 03:18
Оценка:
Здравствуйте, uzhas, Вы писали:

U>тогда так: http://ideone.com/O7AWM


C-style cast в данном случае фактически вызывает const_cast. Правда бывают такие странные code style, которые почему-то запрещают const_cast, но не запрещают C-style cast. В результате программисты пользуются этой лазейкой. Я считают, что при таком подходе, хотя и буква закона соблюдена, дух и смысл закона нарушен. const_cast происходит, хотя он замаскирован другой синтаксической конструкцией.
Re[2]: Как обойтись без const_cast?
От: Андрей Е  
Дата: 15.05.12 03:21
Оценка:
Здравствуйте, Sir-G, Вы писали:

SG>Встречался с такими ситуациями. Мне кажется нормальным сделать const_cast, и написать комментарий рядом, что это для производительности. Типа неконстантый доступ есть, но долго искать объект. Можно еще отладочную проверку сделать, что указатель действительно в массиве есть.

Видимо придётся так и сделать.


Здравствуйте, MasterZiv, Вы писали:

MZ>Но это же враньё, это не для производительности, а по разгильдяйству.

MZ>Зачем врать в комментах?

А в чем состоит разгильдяйство?
Re[2]: Как обойтись без const_cast?
От: Андрей Е  
Дата: 15.05.12 03:28
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Если известно априори, то зачем передавать вектор в функцию? (Правда, если это действительно априорная информация, то значит, что в программе слишком много const расставлено не по делу, либо наоборот, где-то их не хватает).


К>А если по этому вектору как-то пробегают — то заодно можно обнаружить в нём aa[i]==b.getA(), и вызвать aa[i]->process(). Или убедиться, что этого элемента там нет и выругаться.


Это фрагмент кода более сложной программы. На самом деле вектор нам доступен как член данных того же класса, которому принадлежит и фукнция f.
const не может стоять не по делу — программа не будет компилироваться. А вот нехватка вполне возможна.
В рамках этой функции по вектору никто не пробегает. Собственно в рамках этой функции и нужно вызвать process именно для того A, на который ссылается именно этот B. При этом мы находимся вне B и нам доступен неконстантный A, так что константность B вроде как мешать не должна.
Re[3]: Как обойтись без const_cast?
От: Кодт Россия  
Дата: 15.05.12 09:19
Оценка:
Здравствуйте, Андрей Е, Вы писали:

К>>Если известно априори, то зачем передавать вектор в функцию? (Правда, если это действительно априорная информация, то значит, что в программе слишком много const расставлено не по делу, либо наоборот, где-то их не хватает).


К>>А если по этому вектору как-то пробегают — то заодно можно обнаружить в нём aa==b.getA(), и вызвать aa[i]->process(). Или убедиться, что этого элемента там нет и выругаться.


АЕ>Это фрагмент кода более сложной программы. На самом деле вектор нам доступен как член данных того же класса, которому принадлежит и фукнция f.



АЕ>const не может стоять не по делу — программа не будет компилироваться. А вот нехватка вполне возможна.


Ещё как может стоять не по делу. Убери все const из программы — и она снова станет компилироваться так что это не аргумент.

Если ты знаешь, что объект A, переданный в B, нужно [i]будет
менять по ссылке из B, — зачем там константность?
Если в точке конструирования B объект A константный (например, эта точка — константный метод A), — то надо понимать: отдача константного указателя на сторону и в будущее — это обещание, что объект A ни сейчас не меняется, ни в будущем (что, очевидно, не так). Так, может быть, следует снять константность с объекта A прямо там и тогда? Возможно, что отследить всю цепочку, где константность обещана, но не оправдана.

Либо это не указатель, а хэндл, служащий лишь для идентификации объекта.
А с хэндлами разговор или длинный (лезть в таблицу — в тот самый вектор — и искать), или короткий (да хоть reinterpret_cast), если ты знаешь его внутреннюю природу.


АЕ>В рамках этой функции по вектору никто не пробегает. Собственно в рамках этой функции и нужно вызвать process именно для того A, на который ссылается именно этот B. При этом мы находимся вне B и нам доступен неконстантный A, так что константность B вроде как мешать не должна.


Я вот чувствую, что где-то в общем дизайне программы есть недочёт. По крайней мере, пара версий:
— больше, чем 2 (константность/неконстантность) уровня прав использования
— больше, чем 2 (public/private) области доступа
Но это сложно обсуждать, не вдаваясь в подробности. Поэтому сейчас забьём.

Быстрое же решение — вот такое: добавить ассерт.
assert( std::find(aa.begin(), aa.end(), b.getA()) != aa.end() );
const_cast<A*>(b.getA())->process();
Перекуём баги на фичи!
Re[3]: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 15.05.12 09:55
Оценка: +1
On 05/15/2012 07:21 AM, Андрей Е wrote:

> MZ>Но это же враньё, это не для производительности, а по разгильдяйству.

> MZ>Зачем врать в комментах?
>
> А в чем состоит разгильдяйство?

В том, что вместо того, чтобы правильно спроектировать код, ты пишешь горбуху.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 15.05.12 09:58
Оценка: +1
On 05/15/2012 01:19 PM, Кодт wrote:

> Быстрое же решение — вот такое: добавить ассерт.

>
> assert( std::find(aa.begin(), aa.end(), b.getA()) != aa.end() );
> const_cast<A*>(b.getA())->process();

Тут вопрос в другом, -- нафига вообще вызывать эту функцию
process(), которая неконстантная, через класс, содержащий константную ссылку ?
Надо эту проблему решать, а не людей в форуме вопросами мучать.
Posted via RSDN NNTP Server 2.1 beta
Re: Как обойтись без const_cast?
От: jazzer Россия Skype: enerjazzer
Дата: 15.05.12 10:10
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Подскажите пожалуйста какой лучший способ снять константность с указателя на A в функции f?

АЕ>Можно сделать const_cast — но это запрещено в code style.

Бредовый code style, значит — программеры будут изворачиваться через сишный каст со скобками, который увидеть довольно трудно, в отличие от const_cast, который виден в коде, доступен для банального текстового поиска и четко выражает намерения программиста.

АЕ>На текущий момент, я додумался только до того, чтобы искать в aa такое же значение, что и в b и использовать его.

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

АЕ>Может быть в данной ситуации есть какое-то более простое, быстрое и красивое решение? Если вы его знаете, подскажите пожалуйста!

const_cast или, если человек на код-ревью непробиваем, сишный каст.

АЕ>Тем более, что сам класс B никак не меняет экземпляр на который указывает m_a.

У тебя класс В спроектирован так, что может указывать на абсолютно любой объект типа А (и истинно константный в том числе), и гарантирует, что он его менять не будет.
Таким образом код, который работает с произвольным объектом типа В, не имеет права снимать константность.
В твоем же случае у тебя в распоряжении есть дополнительная информация, что объект А, на который указывает пришедший объект В — точно неконстантный.
Эта информация у тебя в коде никак не выражена (ее можно было бы выразить, например, специальным классом В_non_const), так что остается только const_cast и соответствующий комментарий. Плюс в твоем случае есть возможность проверить, что В указывает на объект именно из вектора — это можно сунуть в ассерт в отладочной версии.

В_non_const можно отнаследовать от В и просто добавить GetNonConstA с конст-кастом внутри. В таком случае все, кто ожидает на входе В, его и получат, а те места, которые знают, что там на самом деле В_non_const, будут иметь доступ к неконстантному А. Но возможность такого решения очень сильно зависит от текущих взаимоотношений классов и от того, чем на самом деле занимается В в твоей программе, а не в этом примере, и все может запросто оказаться невозможным.

Еще один вариант выразить явно знание о том, у тебя все объекты лежат в векторе, можно, создав специальный вариант В, который будет держать итераторы внутрь вектора, а не указатели. Или индексы, если вектор меняется (но тогда и нынешнее решение с прямыми указателями не работало бы). Но, опять же, это зависит от того, куда у тебя еще уходит В.
Но если В — это внутренний класс, то убери константность его члена и не парься.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: Как обойтись без const_cast?
От: jazzer Россия Skype: enerjazzer
Дата: 15.05.12 10:13
Оценка:
Здравствуйте, MasterZiv, Вы писали:

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

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

У этой проблемы может оказаться единственное нереалистичное решение "переписать все нафиг"
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Как обойтись без const_cast?
От: S_snowfall  
Дата: 15.05.12 11:06
Оценка:
Ну как вариант pImpl использовать. Храним в B указатель на неконстантный объект A, и без проблем возвращаем его в getA(). Всю реализацию B выносим в BImpl, и там уже используем указатель на константный объект А.
Re[6]: Как обойтись без const_cast?
От: MasterZiv СССР  
Дата: 15.05.12 11:33
Оценка:
> У этой проблемы может оказаться единственное нереалистичное решение "переписать
> все нафиг"

Во-первых, 50/50 -- может оказаться, а может и не оказаться, т.е. есть и ещё
какое-то решение. А 50% -- это уже хороший шанс.
Posted via RSDN NNTP Server 2.1 beta
Re[7]: Как обойтись без const_cast?
От: jazzer Россия Skype: enerjazzer
Дата: 15.05.12 11:37
Оценка:
Здравствуйте, MasterZiv, Вы писали:


>> У этой проблемы может оказаться единственное нереалистичное решение "переписать

>> все нафиг"

MZ>Во-первых, 50/50 -- может оказаться, а может и не оказаться, т.е. есть и ещё

MZ>какое-то решение. А 50% -- это уже хороший шанс.
50% соответствуют твоему опыту? А то у меня вот опыт гораздо более печальный в этом смысле.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.