std::async fails in some msvc versions, workaround?

131 Views Asked by At

The following code:

#include <vector>
#include <algorithm>
#include <thread>
#include <future>
#include <iostream>


int main() {
    std::vector<int> v(1000);
    for(unsigned i = 0; i < v.size(); i++) {
        v[i] = i;
    }
    int bb = 2;
    std::cout << v.back() << std::endl;
    auto f = [&](int& x) {x = 2*x; bb=4; };
    
    std::async(std::launch::async, std::for_each<decltype(v.begin()),decltype(f)>,
           v.begin(), v.end(), f);

    std::cout << v.back() << std::endl;
    std::cout << "bb: " << bb;
    return 0;
}

fails to compile with msvc version < 19.32. e.g. the errors look like this with msvc = 19.29 (compiled on godbolt.org):

example.cpp
<source>(16): warning C4834: discarding return value of function with 'nodiscard' attribute
C:/data/msvc/14.31.31108/include\future(314): error C2280: 'main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37> &main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>::operator =(const main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37> &)': attempting to reference a deleted function
<source>(14): note: see declaration of 'main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>::operator ='
<source>(14): note: 'main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37> &main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>::operator =(const main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37> &)': function was explicitly deleted
C:/data/msvc/14.31.31108/include\future(309): note: while compiling class template member function 'void std::_Associated_state<_Ty>::_Set_value_raw(_Ty &&,std::unique_lock<std::mutex> *,bool)'
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
C:/data/msvc/14.31.31108/include\future(305): note: see reference to function template instantiation 'void std::_Associated_state<_Ty>::_Set_value_raw(_Ty &&,std::unique_lock<std::mutex> *,bool)' being compiled
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
C:/data/msvc/14.31.31108/include\future(722): note: see reference to class template instantiation 'std::_Associated_state<_Ty>' being compiled
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
C:/data/msvc/14.31.31108/include\future(720): note: while compiling class template member function 'std::_State_manager<_Ty>::~_State_manager(void) noexcept'
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
C:/data/msvc/14.31.31108/include\future(879): note: see reference to function template instantiation 'std::_State_manager<_Ty>::~_State_manager(void) noexcept' being compiled
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
C:/data/msvc/14.31.31108/include\future(860): note: see reference to class template instantiation 'std::_State_manager<_Ty>' being compiled
        with
        [
            _Ty=main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>
        ]
<source>(17): note: see reference to class template instantiation 'std::future<main::<lambda_fa7e30e7ff267c277ebbd82c8a7f9e37>>' being compiled

https://godbolt.org/z/WsovKcnnc

It is successfully compiled with gcc, icc, clang and msvc (>=19.32) (using -lpthread).

Is it possible to make it compile with msvc version = 19.29 (VS19) ?

I tried the option /std:c++latest but it did not make a difference.

1

There are 1 best solutions below

0
Marios V On

If Marek R is right about the problem being MSVC's STL implementation, you may be able to avoid calling std::async by using std::packaged_task and std::thread instead.

#include <thread>
#include <future>

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

int main()
{
    std::vector<int> v(100);
    std::iota(v.begin(), v.end(), 0);
    int bb = -2;
    auto f = [&] (int& x) {
        x = 2 * x;
        bb = 4;
    };
    using Iter = decltype(v.begin());
    using Fun = decltype(f);
    auto foreach = [] (Iter begin, Iter end, Fun f) {
        std::for_each<Iter, Fun>(begin, end, f);
    };
    std::packaged_task<void(Iter, Iter, Fun)> task(foreach);
    auto future = task.get_future();
    std::thread t(std::move(task), v.begin(), v.end(), f);

    future.get();
    t.join();
    //
    std::cout << v.back() std::endl;
    std::cout << "bb: " << bb << std::endl;
    return 0;
}