mark
mark

Reputation: 89

template argument deduction/substitution failed in multithreading

I'm running into this template deduction/substitution problem when passing a function through. Could someone help, I'd appreciate it very much:

#include <iostream>
#include <vector>
#include <future>


template< class D > class Region
{

public:

  virtual bool                   setRegion( D& );

protected:


  int                            computeValue( int input, D& dataPack );
  template < class Fn > bool     setRegionValue( const int, D&, Fn&& );
  template < class Fn > bool     setAllValues( D&, Fn&& );

protected:

  std::vector< int >                  m_regionValues;
  std::vector< std::future< bool > >  m_futures;
};



template< class D > int Region< D >::computeValue( const int input,
                                                   D& dataPack )
{
  return input * dataPack.getData();
}


template< class D > 
template < class Fn > bool Region< D >::setRegionValue( const int input,
                                                        D& dataPack,
                                                        Fn&& function )
{
  int output = function( input, dataPack, output );
  m_regionValues.push_back( output * 2 );
  //check..
  return true;
}
  

template< class D > 
template< class Fn > bool Region< D >::setAllValues( D& dataPack,
                                                     Fn&& fn )
{
  for ( int indx = 0; indx < 10; ++indx )
  {
    int input = indx * 5;
    m_futures.push_back( std::async( std::launch::async,
                                     &Region::setRegionValue< std::remove_reference_t< Fn > >,
                                     this, input, std::ref( dataPack ),
                                     std::forward< Fn >( fn ) ) );
  }

  //check thread status..
  return true;
}


template< class D > bool Region< D >::setRegion( D& dataPack )
{
  auto func = std::bind( &Region::computeValue, this,
                         std::placeholders::_1, std::placeholders::_2 );
  
  return setAllValues( dataPack, func );
}


struct Region1Data
{
  int        m_regionData;
  int        getData() const;
};


int Region1Data::getData() const
{
  return m_regionData;
}
  

class Region1: public Region< Region1Data >
{
public:

  bool                         setRegion( Region1Data& );
  void                         outputValue();
};


void Region1::outputValue()
{
  //..
}


bool Region1::setRegion( Region1Data& dataPack )
{
  if ( !Region::setRegion( dataPack ) )
  {
    return false;
  }

  outputValue();
  return true;
}


int main()
{
  Region1 thisRegion;
  Region1Data dataPack;
  dataPack.m_regionData = 10;
  
  thisRegion.setRegion( dataPack );
  return 0;
}

Here are the errors when compiling the code:

example.cpp: In instantiation of 'bool Region::setAllValues(D&, Fn&&) [with Fn = std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&; D = Region1Data]': example.cpp:72:39:   required from 'bool Region::setRegion(D&) [with D = Region1Data]' example.cpp:106:17:   required from here example.cpp:59:63: error: no matching function for call to 'async(std::launch, , Region, int&, std::reference_wrapper, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&)'                                       std::forward< Fn >( fn ) ) );                                                                ^ example.cpp:59:63: note: candidates are: In file included from example.cpp:3:0: /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:75:5: note: template std::future::type> std::async(std::launch, _Fn&&, _Args&& ...)      async(launch __policy, _Fn&& __fn, _Args&&... __args)      ^ /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:75:5: note:   template argument deduction/substitution failed: /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future: In substitution of 'template std::future::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = bool (Region::)(int, Region1Data&, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&&); _Args = {Region, int&, std::reference_wrapper, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&}]': example.cpp:59:63:   required from 'bool Region::setAllValues(D&, Fn&&) [with Fn = std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&; D = Region1Data]' example.cpp:72:39:   required from 'bool Region::setRegion(D&) [with D = Region1Data]' example.cpp:106:17:   required from here /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:75:5: error: no type named 'type' in 'class std::result_of::(Region, int&, std::reference_wrapper, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&))(int, Region1Data&, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&&)>' example.cpp: In instantiation of 'bool Region::setAllValues(D&, Fn&&) [with Fn = std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&; D = Region1Data]': example.cpp:72:39:   required from 'bool Region::setRegion(D&) [with D = Region1Data]' example.cpp:106:17:   required from here /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:95:5: note: template std::future::type> std::async(_Fn&&, _Args&& ...)      async(_Fn&& __fn, _Args&&... __args)      ^ /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:95:5: note:   template argument deduction/substitution failed: /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future: In substitution of 'template std::future::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {bool (Region::)(int, Region1Data&, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&&), Region, int&, std::reference_wrapper, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&}]': example.cpp:59:63:   required from 'bool Region::setAllValues(D&, Fn&&) [with Fn = std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&; D = Region1Data]' example.cpp:72:39:   required from 'bool Region::setRegion(D&) [with D = Region1Data]' example.cpp:106:17:   required from here /opt/devl/optimize/gcc-4.9.3/include/c++/4.9.3/future:95:5: error: no type named 'type' in 'class std::result_of::)(int, Region1Data&, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&&), Region, int&, std::reference_wrapper, std::_Bind::*)(int, Region1Data&)>(Region, std::_Placeholder<1>, std::_Placeholder<2>)>&)>'

Upvotes: 1

Views: 474

Answers (1)

rafix07
rafix07

Reputation: 20949

setAllValues is called with Lvalue:

template< class D > bool Region< D >::setRegion( D& dataPack ) {
  auto func = std::bind( &Region::computeValue, this,
                         std::placeholders::_1, std::placeholders::_2 );

  return setAllValues( dataPack, func );         // func as named object is Lvalue
}

Due to forwarding reference rules Fn is deduced to be Fn& - as Lvalue reference.

template< class D > 
template< class Fn > bool Region< D >::setAllValues( D& dataPack,
                                                     Fn&& fn )          // Fn is Fn& 

In below

&Region::setRegionValue< std::remove_reference_t< Fn > >,
                        ^^^ you put explicitly type in template argument list 

referenceness of Fn is removed. Because you specify explicitly parameter of setRegionValue to be Fn, type of its third parameter is Fn&& - Rvalue ref.
Forwarding Lvalue by forward<Fn>(fn) to Rvalue doesn't match, and that is why the code failed.

To handle Fn as Lvalue you could write:

template< class D > 
template< class Fn > bool Region< D >::setAllValues( D& dataPack,
                                                     Fn&& fn ) {             // Fn& &&fn
  for ( int indx = 0; indx < 10; ++indx )
  {
    int input = indx * 5;
    m_futures.push_back( std::async( std::launch::async,
                                     &Region::setRegionValue< Fn >,          // <- here is Fn&
                                     this, input, std::ref( dataPack ),
                                     std::ref( fn ) ) );                     // <-
  }
  return true;
}

in case of &Region::setRegionValue< Fn >, setRegionValue takes its third parameter as Fn&. Because async takes all its parameters by value (as decayed) you have to use std::ref to wrap Lvalue into std::reference_wrapper.

Working version 1


Above code doesn't handle case when passing Rvalue into setAllValues.

If you wrote:

return setAllValues( dataPack, std::bind( &Region::computeValue, this,
                         std::placeholders::_1, std::placeholders::_2 ) );

definition of setAllValues should be:

template< class D > 
template< class Fn > bool Region< D >::setAllValues( D& dataPack,
                                                     Fn&& fn ) {
  for ( int indx = 0; indx < 10; ++indx )
  {
    int input = indx * 5;
    m_futures.push_back( std::async( std::launch::async,
                                     &Region::setRegionValue< Fn >,
                                     this, input, std::ref( dataPack ),
                                     std::forward<Fn>( fn ) ) );
  }
  return true;
}

in which fn as temporary is just forwarded.

Working version 2


It is hard to accommodate std::ref (which is required by std::async) with std::forward in one call, you could make two overloads:

template < class Fn > bool     setAllValues( D&, Fn&& );   // uses std::forward
template < class Fn > bool     setAllValues( D&, Fn& );    // uses std::ref

to handle both cases.

Upvotes: 1

Related Questions