3

I've implemented a very based ParallelFor algorithm as follows:

inline void ParallelFor( const size_t startIdx, const size_t endIdx, std::function< void( size_t ) >&& fn, const size_t numThreads = std::thread::hardware_concurrency() )
{
    const size_t portion = std::max( 1, (endIdx - startIdx) / numThreads );
    std::vector< std::thread > threads;
    for ( size_t i = startIdx; i < endIdx; i += portion )
    {
        int from    = i;
        int to      = (i + portion) <= endIdx ? (i + portion) : endIdx;

        threads.push_back( std::thread( [=,&fn]() 
            {
                for ( int j = from; j < to; ++j )
                    fn( j );
            } ) );
    }
    std::for_each( threads.begin(), threads.end(), []( std::thread& x ) 
        { 
            x.join();
        } );
}

However when I build it says that the threads.push_back function is "attempting to reference a deleted function".

I can't understand what I'm doing wrong. Can someone point out where I'm going wrong?

EDIT: The compiler is VStudio 2015

There is tonnes more to the error as follows:

1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0(657): error C2280: 'std::thread::thread(const std::thread &)': attempting to reference a deleted function
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\thread(73): note: see declaration of 'std::thread::thread'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0(775): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,const std::thread&>(_Objty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Ty=std::thread,
1>              _Objty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0(775): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,const std::thread&>(_Objty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Ty=std::thread,
1>              _Objty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0(920): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,const std::thread&>(std::allocator<_Ty> &,_Objty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::thread>,
1>              _Ty=std::thread,
1>              _Objty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0(920): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,const std::thread&>(std::allocator<_Ty> &,_Objty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::thread>,
1>              _Ty=std::thread,
1>              _Objty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory(379): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<std::thread,const std::thread&>(_Ty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Ty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory(379): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<std::thread,const std::thread&>(_Ty *,const std::thread &)' being compiled
1>          with
1>          [
1>              _Ty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory(416): note: see reference to function template instantiation '_FwdIt std::_Uninit_copy<_InIt,_FwdIt,std::allocator<_Ty>>(_InIt,_InIt,_FwdIt,std::_Wrap_alloc<std::allocator<_Ty>> &,std::_Nonscalar_ptr_iterator_tag)' being compiled
1>          with
1>          [
1>              _FwdIt=std::thread *,
1>              _InIt=const std::thread *,
1>              _Ty=std::thread
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xmemory(427): note: see reference to function template instantiation '_FwdIt std::_Uninit_copy<const std::thread*,_Iter,_Alloc>(_InIt,_InIt,_FwdIt,_Alloc &)' being compiled
1>          with
1>          [
1>              _FwdIt=std::thread *,
1>              _Iter=std::thread *,
1>              _Alloc=std::_Wrap_alloc<std::allocator<std::thread>>,
1>              _InIt=const std::thread *
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(1672): note: see reference to function template instantiation '_FwdIt std::_Uninitialized_copy<_Iter,std::thread*,std::_Wrap_alloc<std::allocator<_Ty>>>(_InIt,_InIt,_FwdIt,_Alloc &)' being compiled
1>          with
1>          [
1>              _FwdIt=std::thread *,
1>              _Iter=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>,
1>              _Ty=std::thread,
1>              _InIt=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>,
1>              _Alloc=std::_Wrap_alloc<std::allocator<std::thread>>
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(751): note: see reference to function template instantiation 'std::thread *std::vector<std::thread,std::allocator<_Ty>>::_Ucopy<std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>>(_Iter,_Iter,std::thread *)' being compiled
1>          with
1>          [
1>              _Ty=std::thread,
1>              _Iter=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(751): note: see reference to function template instantiation 'std::thread *std::vector<std::thread,std::allocator<_Ty>>::_Ucopy<std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>>(_Iter,_Iter,std::thread *)' being compiled
1>          with
1>          [
1>              _Ty=std::thread,
1>              _Iter=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::thread>>>
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(742): note: while compiling class template member function 'std::vector<std::thread,std::allocator<_Ty>>::vector(const std::vector<_Ty,std::allocator<_Ty>> &)'
1>          with
1>          [
1>              _Ty=std::thread
1>          ]
Goz
  • 61,365
  • 24
  • 124
  • 204
  • What compiler is this, and is there any more to the error? – Dave S Oct 02 '15 at 11:01
  • Possible duplicate of [Error C2280: 'std::thread::thread(const std::thread &)' : attempting to reference a deleted function](http://stackoverflow.com/questions/20723566/error-c2280-stdthreadthreadconst-stdthread-attempting-to-referenc) – Mgetz Oct 02 '15 at 11:03
  • @Mgetz: If there IS an answer at that then can someone please explain it to me because it doesn't seem relevant to my problem (I have already checked it though) – Goz Oct 02 '15 at 11:07
  • 1
    @Goz it's *exactly* your issue, you're attempting to *COPY* a `std::thread` which is move only – Mgetz Oct 02 '15 at 11:08
  • @Mgetz: Except he's not. He's constructing a temporary, which should be going to the rvalue version of push_back. See http://ideone.com/r5rdKF (which uses a different compiler). – Dave S Oct 02 '15 at 11:08
  • @DaveS you're assuming microsoft has a compliant compiler and library (they don't) – Mgetz Oct 02 '15 at 11:12
  • @Goz you don't have [`/Za` set do you?](http://stackoverflow.com/q/2681959/332733) – Mgetz Oct 02 '15 at 11:33
  • @Goz something smells here, I just tested the very basic case with `push_back` on my machine (VS 2015 community) and it works fine. Something else is going on. – Mgetz Oct 02 '15 at 11:45
  • @MGetz: Hmmm ... try sticking it in its own header and see what happens ...? – Goz Oct 02 '15 at 11:49
  • @Goz I've just tried simple captures `atomic_int` and `int` by reference and value respectively and that didn't trigger anything, so I'm beginning to suspect it might be the `std::function` – Mgetz Oct 02 '15 at 11:51
  • @Goz still works, my test code in terms of arguments and passing is identical to yours and works. Are you by chance on a VS2015 beta or RC? – Mgetz Oct 02 '15 at 11:58
  • @Mgetz: Nope .. VS2015 community. Very strange, I have been suffering a whole bunch of weird things happening though ... May be related ... – Goz Oct 02 '15 at 12:01
  • @Goz try repairing your install – Mgetz Oct 02 '15 at 12:13
  • @Mgetz: Already had to do that once today! – Goz Oct 02 '15 at 12:27

1 Answers1

3

I can't immediately see an issue, as it works as is on gcc. I suspect that the compiler isn't calling the proper push_back overload.

I would try using emplace back instead.

  threads.emplace_back(  [=,&fn]() 
             {
                for ( int j = from; j < to; ++j )
                    fn( j );
             }  );

Edit: Since apparently the compiler doesn't support the emplace back either, I would probably fall back to std::vector<std::shared_ptr<std::thread> >, and change the insertion code to

  threads.emplace_back( std::make_shared<std::thread>( [=,&fn]() 
             {
                for ( int j = from; j < to; ++j )
                    fn( j );
             }  ) );

There has to be a change on the join loop as well.

Dave S
  • 20,507
  • 3
  • 48
  • 68
  • @DaveS: Yeah thats one way of dealing with it ... how annoying. Thanks for the help! – Goz Oct 02 '15 at 11:31
  • So I just tested the very basic case that @Goz is doing above, simple lambda into a thread constructor and then `push_back` the temporary. That works – Mgetz Oct 02 '15 at 11:46