LINUX.ORG.RU

История изменений

Исправление eao197, (текущая версия) :

Возможно я неправильно понял твою идею с использованием виртуального метода.

Очевидно, что так.

В TaskBase можно иметь просто виртуальный метод invoke:

class TaskBase {
protected:
  TaskBase() = default;

public:
  virtual ~TaskBase() = default;

  virtual void invoke() = 0;
};

А в наследниках просто переопределять этот invoke:

template<typename Handler>
class Task : public TaskBase {
  Handler fn_;
public:
  template<typename Fn>
  Task(Fn && fn) : fn_{std::forward<Fn>(fn)} {}

  void invoke() override { fn_(); }
};

Зачем все эти приседания с указателем на функцию, дополнительные статические методы со static_cast внутри?

Ну и еще вам в TaskBase, скорее всего, потребуется виртуальный деструктор.

Возможно. Зависит от имплементации.

Зависит от имплементации очереди тасков. Т.к. если вы будете хранить unique_ptr<TaskBase>, то проблемы из-за отсутствия виртуального деструктора практически гарантированы.

Если же будет использоваться shared_ptr<TaskBase>, то подобных проблем можно избежать. Но это зависит от кривизны рук программиста.

Так что лучше сделать виртуальный деструктор. А отказаться от него только если профайлер покажет, что есть просадка производительности из-за этого виртуального вызова (что вряд ли будет иметь место в 99% случаев).

Исходная версия eao197, :

Возможно я неправильно понял твою идею с использованием виртуального метода.

Очевидно, что так.

В TaskBase можно иметь просто виртуальный метод invoke:

class TaskBase {
protected:
  TaskBase() = default;

public:
  virtual ~TaskBase() = default;

  virtual void invoke() = 0;
};

А в наследниках просто переопределять этот invoke:

template<typename Handler>
class Task : public TaskBase {
  Handler fn_;
public:
  template<typename Fn>
  Task(Fn && fn) : fn_{std::forward<Fn>(fn)} {}

  void invoke() override { fn_(); }
};

Зачем все эти приседания с указателем на функцию, дополнительные статические методы со static_cast внутри?

Ну и еще вам в TaskBase, скорее всего, потребуется виртуальный деструктор.

Возможно. Зависит от имплементации.

Зависит от имплементации очереди сообщений. Т.к. если вы будете хранить unique_ptr<TaskBase>, то проблемы из-за отсутствия виртуального деструктора практически гарантированы.

Если же будет использоваться shared_ptr<TaskBase>, то подобных проблем можно избежать. Но это зависит от кривизны рук программиста.

Так что лучше сделать виртуальный деструктор. А отказаться от него только если профайлер покажет, что есть просадка производительности из-за этого виртуального вызова (что вряд ли будет иметь место в 99% случаев).