Search code examples
c++asynchronousfuturestdbind

no type named 'type' in class std::result_of


The following code just wouldn't compile:

template< typename Fn >  
bool templateFunctionOne( Fn&& fn )
{
   int v = 5;
   return fn( v );
}

template < typename Fn >
bool templateFunctionTwo( Fn&& fn )
{
   std::future< bool > tk( std::async( std::launch::async,
                           &templateFunctionOne< Fn >,
                           std::forward<Fn>(fn ) ) );
   return tk.get();
 }

 bool printThis( int value )
 {
   cout << value << endl;
   return true;
 }

 int main()
 {
    auto func = std::bind( &printThis, std::placeholders::_1 );
    return templateFunctionTwo( func );  
 }  

when compiling it gives the following error:

functional::1665:61: error: no type named 'type' in 'class std::result_of< bool ((std::_Bind(std::_Placeholder<1>))(int)>))(std::_Bind))(int)>&)>'

I simplified the above, still it won't compile with the same error message:

template< typename Fn >  
bool templateFunctionOne( Fn&& fn )
{
   return fn();
}

template < typename Fn >
bool templateFunctionTwo( Fn&& fn )
{
   std::future< bool > tk( std::async( std::launch::async,
                           &templateFunctionOne< Fn >,
                           std::forward<Fn>(fn ) ) );
   return tk.get();
 }

 bool printThis()
 {
   return true;
 }

 int main()
 {
    auto func = std::bind( &printThis );
    return templateFunctionTwo( func );  
 }  

Since now the function printThis doesn't require passing in any parameters, binding the call isn't necessary. And when changing the call in the main as follows, it compiles fine:

 int main()
 {
    return templateFunctionTwo( &printThis );  
 }  

Could someone help to explain? I've seen the same kind of errors when passing a function pointer without using std::ref to wrap a parameter when its reference is required, but this seems to be something else (or not), what am I missing here?


Solution

  • auto func = std::bind( &printThis );
    return templateFunctionTwo( func );  
    

    func is Lvalue, so when Lvalue is passed into templateFunctionTwo , Fn is deduced to be Fn& (due to forwarding reference).

    Below line

    &templateFunctionOne< Fn >,
    

    means

    &templateFunctionOne< Fn& >,
    

    it implies that templateFunctionOne takes its argument by reference, if you want to pass argument by reference when calling async you need to use wrapper std::ref or std::cref:

       std::future< bool > tk =  std::async( 
                   templateFunctionOne< Fn >,
                   std::ref( std::forward<Fn>(fn) ) ); // <--- ref added
    

    Now your code compiles.

    Another way is to use remove_reference_t<Fn> or typename std::remove_reference<Fn>::type to remove Lvalue reference from Fn:

      std::future< bool > tk =  std::async( 
         templateFunctionOne< std::remove_reference_t<Fn> >, // <--- remove_reference_t added 
         std::forward<Fn>(fn)  );
         return tk.get();
    

    then fn object is passed by value. This version is better than first, because the call

     templateFunctionTwo( std::bind( &printThis, std::placeholders::_1 ) )
    

    fails when ref is used, because ref needs to take only Lvalues.