#cpp #qt #concurrency #шаблоны_с++
Есть шаблонная функция: templateauto exceptionWrapper (Object *obj, Function&& function, Args&& ...args)-> decltype((obj->*function)(std::forward (args)...)) { try { QScopedPointer
Ответы
Ответ 1
exceptionWrapper сам по себе не является функцией, но неким шаблоном функции, поэтому мы не можем никуда его передать — мы можем передавать лишь что-то конкретное, а не абстракцию, коей является шаблон. Таким образом, нам нужно конкретизировать (инстанциировать) exceptionWrapper прежде чем передавать его в QtConcurrent::run. Здесь встаёт другая проблема: с какими аргументами инстанциировать? Т.к. exceptionWrapper использует пробрасывающую ссылку (forwarding reference), идёт расчёт на то, что аргументы будут выводиться при вызове функции, но у нас ситуация иная — мы явно задаём аргументы шаблона. Конечно, мы всё равно получаем нужный результат таким вызовом: QtConcurrent::run(exceptionWrapper, obj, std::forward (query), std::forward (args)...); Происходит это из-за того, что у нас в callAsync пробрасывающая ссылка, а в exceptionWrapper, в результате явной инстанциации, у нас rvalue-ссылка, и применяя правила C++ по свертке ссылок мы на выходе имеем то, что и ожидали: будет инстанциирована правильная, т.е. ожидаемая нами функция exceptionWrapper. К примеру, если мы рассмотрим минимальный пример, из Вашего дополненного вопроса, то при таком вызове: int var = 5; callAsync(obj, &Object::sum, 7, var); Будет сгенерирована функция exceptionWrapper с такой сигнатурой: int (Object *,int (Object::* )(int,int),int &&,int &) — как раз то, что и ожидалось. Но в этом кроется другая проблема. Мы работаем с конкурентной средой и пытаемся вызвать метод Qt, который потенциально выполнит наш метод в другом потоке. Но, чтобы выполнить метод в другом потоке, он вынужден скопировать (либо же переместить, если умеет) аргументы и сохранить их до того времени, когда можно будет вызвать нашу функцию. Сохраняя наши аргументы, Qt отбрасывает их изначальный тип и впоследствии, при передаче их в нашу функцию, все наши аргументы станут lvalue. Но в сигнатуре нашей функции есть int&&, который является rvalue-ссылкой и lvalue не может быть передано туда! Поэтому, нам придётся отказаться от пробрасывающих ссылок в exceptionWrapper — они там не нужны и только мешают, там нужен обычный const &. В результате получим такое решение: template auto fcn(Object *object, Function function, const Args& ...args) { return (object->*function)(args...); } template void callAsync(Object *object, Function&& function, Args&& ...args) { QtConcurrent::run(fcn , object, function, std::forward (args)...); } Конечно, можно подумать и над альтернативными решениями, но этот ответ уже и так довольно длинный получился.
Комментариев нет:
Отправить комментарий