Возвращаемые типы функций-членов C ++ и производные классы - PullRequest
9 голосов
/ 16 февраля 2011

Учитывая этот надуманный пример:

struct point_2d {
  point_2d& x( int n ) {
    x_ = n;
    return *this;
  }

  point_2d& y( int n ) {
    y_ = n;
    return *this;
  }

  int x_, y_;
};

struct point_3d : point_2d {
  point_3d& z( int n ) {
    z_ = n;
    return *this;
  }

  int z_;
};

int main() {
  point_3d p;
  p.x(0).y(0).z(0); // error: "point_2d" has no member named "z"
  return 0;
}

Идея состоит в том, чтобы использовать "цепочку функций-членов", чтобы иметь возможность вызывать более одной функции-члена подряд. (Есть много примеров этого; приведенный выше является самым коротким, о котором я мог подумать, чтобы задать этот вопрос. Моя настоящая проблема похожа и описана ниже.)

Проблема в том, что если производный класс добавляет свои собственные функции-члены цепочки, но сначала вы вызываете функцию-член базового класса, вы получаете ссылку на базовый класс, которая, конечно, не будет работать для вызова функции-члена производного класса. .

Есть ли какие-нибудь умные способы решить эту проблему и при этом сохранить способность выполнять функции-члены?


Актуальная проблема

Моя актуальная проблема в том, что мой базовый класс является исключением, а мой производный класс является классом, производным от базового исключения. Также для этих классов я хочу использовать цепочку функций-членов:

class base_exception : public std::exception {
  // ...
  base_exception& set_something( int some_param ) {
    // ...
    return *this;
  }
};

class derived_exception : public base_exception {
  // ...
};

int main() {
  try {
    // ...
    if ( disaster )
      throw derived_exception( required_arg1, required_arg2 )
            .set_something( optional_param );
  }
  catch ( derived_exception const &e ) {
    // terminate called after throwing an instance of 'base_exception'
  }
}

Проблема в том, что set_something() возвращает base_exception, но catch ожидает derived_exception. Конечно, человек может сказать, что фактическим типом исключения является derived_exception, но компилятор, очевидно, не может сказать.

Это проблема, которую я действительно пытаюсь решить, т. Е. Как сделать так, чтобы базовый класс исключений мог устанавливать необязательные параметры для объекта исключения, но возвращал экземпляр производного типа. Пример point_2d, который я привел выше, является (я полагаю) уменьшенной и более простой версией той же проблемы, которую люди могут понять, и что решение меньшей проблемы также решит мою реальную проблему.

Обратите внимание, что я подумал о том, чтобы сделать base_exception шаблоном и передать производный тип, например:

template<class Derived>
class base_exception {
  // ...
  Derived& set_something( int some_param ) {
    // ...
    return *this;
  }
};

Я считаю, что это действительно решает проблему, но это не идеальное решение, потому что если другой класс more_derived_exception является производным от derived_exception, то мы вернемся к той же проблеме.

Ответы [ 3 ]

7 голосов
/ 16 февраля 2011

То, что вы ищете, это Имена именованных параметров , которую я копирую из этого ответа StackOverflow . Вместо того, чтобы возвращать ссылку на реальный объект, вы возвращаете ссылку на объект специального параметра и полагаетесь на конструктор для вашего объекта исключения, чтобы выполнить неявное преобразование после того, как все параметры заполнены. Это довольно умно, действительно. *

1 голос
/ 24 мая 2011

Привет, у меня только что была похожая проблема, и здесь Мое решение:

template<class DerivedOptions>
class SomeOptions
{
  private:
    DerivedOptions* derived;
    int param1_;
  public:
    SomeOptions()
    {
        derived = reinterpret_cast<DerivedOptions*>(this);
    }

    DerivedOptions & set_some_options(int param1)
    {
        param1_ = param1;
        return *derived;
    }
};

struct MoreOptions: public SomeOptions<MoreOptions>
{
  private:
    int more_;
  public:
    MoreOptions & set_more_options(int more)
    {
        more_ = more;
        return *this;
    }
};

Определенно содержит то, что я знаю, что я делаю, но с другой стороны (по крайней мере, в моем приложении) базаКласс не предназначен для использования без наследования.

С уважением, Regi

0 голосов
/ 16 февраля 2011

Почему бы вам не пойти на самый простой подход (возможно, не самый элегантный):

if ( disaster )
{
    derived_exception e = derived_exception( required_arg1, required_arg2 );
    e.set_something( optional_param );
    throw e;
}

Разве это не решит вашу проблему, или я что-то упущу?

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