Я взглянул на "Функциональную" документацию класса в Повышении и споткнулся через это:
boost::function<float (int x, int y)> f;
Я должен признать, что этот синтаксис очень сбивает с толку меня. Как это может быть легальным C++?
Есть ли под капотом какой-либо прием? Этот синтаксис документируется где-нибудь?
[Редактировать] Это ответ на оригинальный, неотредактированный вопрос автора, который на самом деле состоял из двух вопросов.
Я должен признать, что этот синтаксис очень смущает меня. Как это может быть легальный C++? :) Есть ли какая-нибудь хитрость под капотом? Этот синтаксис документирован где-нибудь?
Это совершенно законно и на самом деле не слишком сложно.
template <class T>
class C
{
public:
T* function_pointer;
};
void fn(int x)
{
cout << x << endl;
}
int main(int argc, char** argv)
{
C<void (int x)> c;
c.function_pointer = &fn;
c.function_pointer(123); // outputs x
}
По сути это то же самое, что сделать:
typedef void Function(int);
C<Function> c;
Этот тип применим не только в C++, он так же применим и в C (фактический тип, к которому параметризован C). Магия шаблона здесь заключается в том, что мы берем что-то вроде типа Function typedef и можем определить типы возвращаемых значений и аргументов. Объяснять это здесь было бы слишком долго, и boost::function использует множество мета-шаблонов функций в boost для получения этой информации. Если вы действительно хотите потратить время на изучение этого, вам следует попытаться понять реализацию boost, начиная с boost::function_traits в Boost.Type Traits.
Однако я хочу рассмотреть вашу общую проблему. Мне кажется, вы слишком сильно пытаетесь упростить пару строк вполне приемлемого кода. Нет ничего плохого в том, чтобы передавать аргументы для подклассов команд через их параметризованные конструкторы подклассов. Стоит ли ради этого пытаться задействовать типовые списки и boost::function-подобные решения, тем самым значительно увеличивая время компиляции и сложность кода?
Если вы хотите сократить его еще больше, просто напишите функцию execute, которая будет выполнять любой подкласс команды и добавлять его в стек отмены и так далее:
typedef boost::shared_ptr<Command> CommandPtr;
void execute(const CommandPtr& cmd)
{
cmd->execute();
// add command to undo stack or whatever else you want to do
}
// The client can simply write something like this:
execute(CommandPtr(new CmdAdd(some_value) );
Я действительно думаю, что попытка усложнить код не стоит того. Авторы boost хотели написать чрезвычайно универсальное решение для boost::function, которое будет использоваться многими людьми на многих платформах и компиляторах. Однако они не пытались обобщить систему команд, способную выполнять функции с различными сигнатурами в рамках единой системы отмены (тем самым требуя, чтобы состояние этих команд сохранялось даже после первоначального завершения их вызова и чтобы можно было отменить и повторно выполнить их без повторного указания исходных данных состояния при последующих выполнениях). Для этого ваш подход, основанный на наследовании, скорее всего, является лучшим и наиболее простым.