Вызов константной функции из неконстантного объекта - PullRequest
21 голосов
/ 02 января 2009

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

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 в течение некоторого времени.

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

Ответы [ 11 ]

14 голосов
/ 04 января 2009

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(); }

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

   const Bar* _this = this;
   _this->getProcess().doSomeWork();
10 голосов
/ 02 января 2009

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

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

4 голосов
/ 02 января 2009

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

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

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

и

getProcess().doSomeWork();

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

3 голосов
/ 09 января 2009

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

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

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

as_const().getProcess().doSomeWork();
1 голос
/ 06 декабря 2012

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

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

и звоните

addConst( getProcess() ).doSomeWork();
1 голос
/ 02 января 2009

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

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

РЕДАКТИРОВАТЬ: Я не прочитал весь вопрос. Да, вам нужно const_cast указатель this или заставить функцию doOtherWork const вызвать const IProcess & getProcess () const .

Суть в том, что вам не нужен объект const для вызова doSomeWork . Так как это цель, вам нужен метод const, называемый?

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

0 голосов
/ 02 января 2009

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

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

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

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

0 голосов
/ 02 января 2009

Я предполагаю, что вы хотите, чтобы 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     
  }
};

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

0 голосов
/ 02 января 2009

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

const IProcess* getProcessConst() const { return ... }
...
getProcessConst().doSomeWork();
0 голосов
/ 02 января 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...