Ошибка C2280 при сборке с MSVC и пакетом параметров

Имеется код:

#include <iostream>
#include <functional>
#include <future>
#include <deque>

template<class T>
struct Caller;

template<class R, class... Args>
struct Caller<R(Args...)>
{
    using PackagedTask = std::packaged_task<R(Args...)>;
    using InternalTask = std::packaged_task<void(void)>;
     
    void enque(PackagedTask&& task, Args&&... args)
    {
        auto fnc = [](auto&& task, auto&&... args) -> void
        { if (task.valid()) task(std::forward<Args>(args)...); };

        auto bnd = std::bind(std::move(fnc),
            std::forward<PackagedTask>(task),
            std::forward<Args>(args)...);

        m_tasks.emplace_back(std::move(bnd));
    }
    
    void run()
    {
        if (!m_tasks.empty())
        {
            auto& task = m_tasks.front();
            if (task.valid()) task();
            m_tasks.pop_front();
        }
    }
    
    std::deque<InternalTask> m_tasks;
};

int main()
{
    std::packaged_task<int(int)> task
    {
        [](auto a)
        {
            std::cout << "tra-ta-ta: " << a << std::endl;
            return 5;
        }
    };
    
    Caller<int(int)> c;
    c.enque(std::move(task), 5);
    c.run();
}

С компилятором GCC собирается без ошибок, MSVC же выдаёт C2280. Прошу помощи разобраться, что именно ему не нравится и как это можно исправить?

*UPD Судя по всему, проблема связана с этим. И решения нет.


Ответы (1 шт):

Автор решения: Arty

Как правильно замечено по ссылке, это баг MSVC компилятора, который пофиксили и запланировали исправить на следующем major релизе.

Как было уже сказано, одно из возможных решений, чтобы обойти это ограничение - это обернуть std::packaged_task в умный указатель (через std::make_shared). Если среди остальных параметров (args) бывают также std::packaged_task объекты, то их тоже надо будет обернуть.

Код ниже работает и под Windows (MSVC) и под Linux (CLang/G++). Только обязательно условие под Linux это добавление опции -pthread, без неё std::packaged_task бросает исключение std::system_error "Unknown error -1".

Рабочий код ниже (оборачивает и task и args):

Попробовать на онлайн Windows MSVC!

Попробовать на онлайн Linux G++/CLang!

#include <iostream>
#include <functional>
#include <future>
#include <deque>
#include <memory>

template<class T>
struct Caller;

template<class R, class... Args>
struct Caller<R(Args...)>
{
    using PackagedTask = std::packaged_task<R(Args...)>;
    using InternalTask = std::packaged_task<void(void)>;
     
    void enque(PackagedTask&& task, Args&&... args)
    {
        auto fnc = [](auto&& task, auto&&... args) -> void
        { if (task->valid()) (*task)(std::forward<Args>(*args)...); };

        auto bnd = std::bind(std::move(fnc),
            std::make_shared<PackagedTask>(std::forward<PackagedTask>(task)),
            std::make_shared<Args>(std::forward<Args>(args))...);
            
        m_tasks.emplace_back(std::move(bnd));
    }
    
    void run()
    {
        if (!m_tasks.empty())
        {
            auto& task = m_tasks.front();
            if (task.valid()) task();
            m_tasks.pop_front();
        }
    }
    
    std::deque<InternalTask> m_tasks;
};

int main()
{
    std::packaged_task<int(int)> task
    {
        [](auto a)
        {
            std::cout << "tra-ta-ta: " << a << std::endl;
            return 5;
        }
    };
    
    Caller<int(int)> c;
    c.enque(std::move(task), 5);
    c.run();
}
→ Ссылка