For anyone interested, a little helper class that allows creating workers with automated variadic argument deduction. Note that C++17 is required (i.e. by which specification of the template arguments aren't necessary). For the full source code see: https://github.com/Broekman/Qt5_template
worker.hpp
#ifndef QT5_UI_WORKER_HPP
#define QT5_UI_WORKER_HPP
#include <QObject>
#include <QString>
#include <functional>
#include <tuple>
namespace ui
{
    class worker_object :
            public QObject
    {
    Q_OBJECT
    public:
        inline worker_object() = default;
        inline ~worker_object() = default;
    public slots:
        inline virtual void run() { /*...*/ };
    signals:
        void finished();
        void error(const QString& err_msg);
    };
    namespace helper
    {
        template <int... Is>
        struct index {};
        template <int N, int... Is>
        struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
        template <int... Is>
        struct gen_seq<0, Is...> : index<Is...> {};
    }
    template<typename... Ts>
    class worker :
            public worker_object
    {
    public: /* Functions */
        template<typename Func, typename... Args>
        inline worker(Func&& fn, Args&& ... args) :
                fn_(std::forward<Func>(fn)),
                args_(std::forward<Args>(args)...)
        { /*...*/ }
        inline ~worker() = default;
        inline void run() override
        {
            func(args_);
            emit finished();
        }
    private: /* Functions */
        template<typename... Args, int... Is>
        inline void func(std::tuple<Args...>& tup, helper::index<Is...>)
        {
            fn_(std::get<Is>(tup)...);
        }
        template<typename... Args>
        inline void func(std::tuple<Args...>& tup)
        {
            func(tup, helper::gen_seq<static_cast<int>(sizeof...(Args))>{});
        }
    private: /* Class members */
        std::function<void(Ts...)> fn_;
        std::tuple<Ts...> args_;
    };
    /**
     * @brief Helper function to create a worker by which specification of the template arguments aren't necessary.
     */
    template<typename Func, typename... Args>
    worker<Args...> make_worker(Func&& fn, Args&& ... args)
    {
        return worker<Args...>(std::forward<Func>(fn), std::forward<Args>(args)...);
    }
}
#endif
main_window.cpp
    void main_window::start_worker(worker_object *thread_worker, const worker_callback& on_finish,
                                   const worker_error_callback& on_error)
    {
        auto *worker_thread = new QThread;
        thread_worker->moveToThread(worker_thread);
        connect(thread_worker, &worker_object::error, this, on_error);
        connect(worker_thread, &QThread::started, thread_worker, &worker_object::run);
        connect(thread_worker, &worker_object::finished, worker_thread, &QThread::quit);
        connect(thread_worker, &worker_object::finished, this, on_finish);
        connect(thread_worker, &worker_object::finished, thread_worker, &worker_object::deleteLater);
        connect(worker_thread, &QThread::finished, worker_thread, &QThread::deleteLater);
        worker_thread->start();
    }
main_window.cpp example 1: no arguments
void main_window::service_example()
{
    //STEP 1: specify a (synchronous) task for the worker.
    auto *work = new worker(make_worker([this]()
    {
        auto task_len_ms = 2500; //ms
        logger_->info("Doing some concurrent work for " + std::to_string(task_len_ms) + " milliseconds...");
        QThread::msleep((unsigned)task_len_ms);
    }));
    //STEP 2: specify the completion handler. Called upon a worker_object::finished signal.
    auto on_finish_callback = [this]()
    {
        logger_->info("Concurrent work finished!");
    };
    //STEP 3: specify an error handler. Called upon a worker_object::error(const QString&) signal.
    auto on_error_callback = [this](const QString& err_msg)
    {
        logger_->error(err_msg.toStdString());
    };
    //STEP 4: start the worker.
    start_worker(work, on_finish_callback, on_error_callback);
}
main_window.cpp example 2: some arguments
//STEP 1: specify a (synchronous) task for the worker.
auto *work = new worker(make_worker([this](const std::string& personal_msg, unsigned long wait_time)
{
    logger_->info(personal_msg);
    QThread::msleep((unsigned)wait_time);
}, "Hello, world?", 2500));
//STEP 2: specify the completion handler. Called upon a worker_object::finished signal.
//STEP 3: specify an error handler. Called upon a worker_object::error(const QString&) signal.
//STEP 4: start the worker.