C++ странный синтаксис определяется в параметрах шаблона Boost

Я взглянул на "Функциональную" документацию класса в Повышении и споткнулся через это:

boost::function<float (int x, int y)> f;

Я должен признать, что этот синтаксис очень сбивает с толку меня. Как это может быть легальным C++?

Есть ли под капотом какой-либо прием? Этот синтаксис документируется где-нибудь?

8
задан Luc Touraille 2 December 2011 в 09:23
поделиться

1 ответ

[Редактировать] Это ответ на оригинальный, неотредактированный вопрос автора, который на самом деле состоял из двух вопросов.

Я должен признать, что этот синтаксис очень смущает меня. Как это может быть легальный 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, которое будет использоваться многими людьми на многих платформах и компиляторах. Однако они не пытались обобщить систему команд, способную выполнять функции с различными сигнатурами в рамках единой системы отмены (тем самым требуя, чтобы состояние этих команд сохранялось даже после первоначального завершения их вызова и чтобы можно было отменить и повторно выполнить их без повторного указания исходных данных состояния при последующих выполнениях). Для этого ваш подход, основанный на наследовании, скорее всего, является лучшим и наиболее простым.

9
ответ дан 5 December 2019 в 18:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: