Вызов константной функции из неконстантного объекта

Мне нужно вызвать константную функцию из неконстантного объекта. См. Пример

struct IProcess {
   virtual bool doSomeWork() const = 0L;
};
class Foo : public IProcess {    
  virtual bool doSomeWork() const {
    ...
  }
};

class Bar
{
public:
   const IProcess& getProcess() const {return ...;}
   IProcess& getProcess() {return ...;}

   void doOtherWork {
    getProcess().doSomeWork();        
  }
};

Звонок

getProcess().doSomeWork();

всегда приводит к вызову

IProcess& getProcess()

Есть ли другой способ вызвать

const IProcess& getProcess() const 

из непостоянной функции-члена? Я до сих пор использовал

const_cast<const Bar*>(this)->getProcess().doSomeWork();

что делает свое дело, но кажется слишком сложным.


Обновлено: я должен упомянуть, что код реорганизуется, и в конечном итоге останется только одна функция.

const IProcess& getProcess() const 

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

Пожалуйста, продолжайте тему.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
22
0
27 099
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Вам не нужно проделывать какие-либо уловки с кастингом, если функция не перегружена. Вызов константного метода неконстантного объекта - это нормально. Он вызывает неконстантный метод из объекта const, что запрещено. Если методы переопределены константной и неконстантной функцией, то приведение объекта к константе поможет:

const_cast<const IProcess&> (getProcess()).doSomeWork();

Обновлено: я не прочитал весь вопрос. Да, вам нужно преобразовать указатель это в const_cast или сделать функцию doOtherWork const для вызова const IProcess и getProcess () const.

Дело в том, что вам не нужен константный объект для вызова поработай немного. Поскольку это цель, нужен ли вам вызываемый метод const?

Другой вариант - переименовать замещаемые функции. Это было бы действительно хорошей идеей, если бы две функции действительно имели разное поведение / побочные эффекты. В противном случае эффект от вызова функции был бы не очевиден.

Я пробовал это перед публикацией. К моему удивлению, неконстантная функция все равно была вызвана. Я еще раз подтвердю это в понедельник. Я работаю в MS VS6 - может быть проблема с компилятором. Спасибо за быстрый ответ.

Chris 02.01.2009 18:41

Есть ли побочные эффекты от функции getProcess? В противном случае какое значение имеет тот, который вызывается?

Judge Maygarden 02.01.2009 18:47

На самом деле, как указал mfazekas, в этом случае достаточно использовать static_cast <> вместо const_cast <>, поскольку вы добавляете, а не удаляете константность, что всегда является безопасной операцией.

j_random_hacker 09.01.2009 11:38

Ну, ты можешь объявить

void doOtherWork const ()

?

Это сделало бы это.

К сожалению нет. doOtherWork () не должен быть постоянным.

Chris 02.01.2009 18:43

Тогда может быть здесь неправильное решение. В чем разница между getProcess () const и plain? Это кеширование? Если так, официальное решение «изменяемое».

user3458 05.01.2009 16:22

Вы в основном застряли с переименованием другого метода или const_cast.

Кстати, это одна из причин, по которой интеллектуальные указатели копирования при записи на самом деле не работают в C++. Интеллектуальный указатель копирования при записи - это тот, который может использоваться бесконечно. Когда пользователь обращается к данным в неконстантном контексте, создается копия данных (если у пользователя нет уникальной ссылки). Этот вид указателя может быть очень удобен при совместном использовании больших структур данных, которые нужно изменять только некоторым клиентам. Самая «логичная» реализация - иметь константный и неконстантный оператор->. Версия const просто возвращает базовую ссылку. Неконстантная версия выполняет уникальную проверку ссылок и копирование. Это бесполезно, потому что неконстантный умный указатель будет использовать неконстантный оператор-> по умолчанию, даже если вы хотите использовать константную версию. Требование const_cast делает его очень недружелюбным для пользователя.

Я приветствую всех, кто доказывает мою неправоту и показывает удобный для пользователя указатель копирования при записи на C++ ...

Ваше решение все равно не работает; smart_cow_ptr <T> не может знать, есть ли у T изменяемые члены.

MSalters 02.01.2009 18:45

Суть примера в том, что он не работает полезным образом. Если вы используете const_cast <smart_cow_ptr <T> &> (ptr) -> some_const_member (), он будет работать нормально, но требование выполнить const_cast IMHO делает использование указателя коровы слишком болезненным.

Mr Fooz 03.01.2009 01:50

Я думаю, что метод const_cast - ваш лучший вариант. Это просто ограничение фреймворка const в C++. Я думаю, что единственный способ избежать кастинга - это определить метод, который независимо возвращает экземпляр const IProcess. Например.

const IProcess* getProcessConst() const { return ... }
...
getProcessConst().doSomeWork();

Если getProcess() и getProcess() const не возвращают ссылку на один и тот же объект (но с разной квалификацией), это указывает на плохую конструкцию class Bar. Перегрузка функции из-за const - не лучший способ различать функции с разным поведением.

Если они возвращают ссылку на тот же объект, тогда:

const_cast<const Bar*>(this)->getProcess().doSomeWork();

а также

getProcess().doSomeWork();

вызывать точно такую ​​же функцию doSomeWork(), поэтому нет необходимости использовать const_cast.

«Копировать при записи» - это пример, когда константа / не константа может возвращать разные объекты, так что это не всегда плохой дизайн.

mfazekas 04.01.2009 18:06

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

CB Bailey 09.01.2009 11:13

Я предполагаю, что вы хотите, чтобы DoOtherWork вызывал один из двух ваших вызовов getprocess в зависимости от того, вызывается он из объекта const или нет.

Лучшее, что я могу предложить:

class Bar
{
public:
   const IProcess& getProcess() const {return ...;}
   IProcess& getProcess() {return ...;}

   void doOtherWork {            // should use getProcess()      
    getProcess().doSomeWork();        
  }
   void doOtherWork const {
    getProcess().doSomeWork();   // should use getProcess() const     
  }
};

Даже если это сработает, мне кажется, это неприятный запах. Я бы очень опасался, что поведение класса радикально изменится в зависимости от константности объекта.

есть только одно определение каждого - void doOtherWork () - virtual bool doSomeWork () const

Chris 02.01.2009 19:00

Posted by monjardin
Another option would be to rename the over-ridden functions. This would be a really good idea if the two function actually have different behavior/side-effects. Otherwise, the effect of the function call would not be obvious.

Доступ к IProcess & осуществляется в другом коде в основном через свойство

__declspec(property(get=getProcess)) IProcess& Process;

так что переименование было не вариант. В большинстве случаев constness вызывающей функции совпадает с getProcess (), поэтому проблем не было.

Ответ принят как подходящий

Избегайте приведения: назначьте это const Bar * или чему-то еще и используйте это для вызова getProcess().

Для этого есть несколько педантичных причин, но это также делает более очевидным то, что вы делаете, не заставляя компилятор делать что-то потенциально опасное. Конечно, вы можете никогда не попасть в эти случаи, но с таким же успехом можете написать что-нибудь, что в этом случае не использует приведение.

О каких педантичных доводах вы говорите? Я бы подумал, что большое уродливое приведение проясняет ваши намерения, чем присвоение const Bar *, но это только мое мнение.

j_random_hacker 09.01.2009 11:17

Единственный способ, которым это имеет тенденцию вызывать неожиданное поведение, - это когда возвращаемое значение getProcess () изменяет свой квалификатор cv (т.е. становится изменчивым). Const_cast <> будет успешным, но он будет делать что-то совсем другое по сравнению с исходным намерением. Назначение поймает это. MSN

MSN 10.01.2009 02:14

const_cast предназначен для приведения константности далеко!

Вы выполняете преобразование из неконстантного в константный, что безопасно, поэтому используйте static_cast:

   static_cast<const Bar*>(this)->getProcess().doSomeWork();

Я имею в виду, что с технической точки зрения вы можете привести к константности с помощью const_cast, но это не прагматичное использование оператора. Цель приведений нового стиля (по сравнению со старым приведением стиля c) состоит в том, чтобы передать намерение приведения типов. const_cast - это запах кода, и его использование следует как минимум пересмотреть. static_cast с другой стороны безопасен. Но это вопрос стиля C++.

Или вы можете создать новый (частный) метод const и вызвать его из doOtherWork:

  void doSomeWorkOnProcess() const { getProcess().doSomeWork(); }

Также можно использовать временную константу (ответ «MSN»):

   const Bar* _this = this;
   _this->getProcess().doSomeWork();

На самом деле вы можете безопасно сделать и то, и другое - отбросить и добавить константность.

Chris 05.01.2009 13:14

Да, но точка зрения mfazekas заключается в том, что вы используете только нужно const_cast <> для удаления константности, и в ваших собственных интересах использовать «наименьшее оружие, которое выполнит эту работу» (то есть static_cast <>), поскольку таким образом все вхождения const_cast это запахи кода, которые можно легко найти.

j_random_hacker 09.01.2009 10:56

Если приведение слишком уродливо для вас, вы можете вместо этого добавить к Bar метод, который просто возвращает константную ссылку на *this:

Bar const& as_const() const {
    return *this;    // Compiler adds "const" without needing static_cast<>
}

Затем вы можете вызвать метод любойconst в Bar, просто добавив as_const()., например:

as_const().getProcess().doSomeWork();

определить шаблон

template< class T >
const T & addConst ( T & t ) 
{
    return t;
}

и позвони

addConst( getProcess() ).doSomeWork();

Другие вопросы по теме