0

I am trying to compile the following program.

#include <iostream>
#include <thread>

struct State
{
    State (std::string message) : message (message)
    {
    }

    template <class Function, class... Args>
    void
    spawn (Function&& function, Args&&... args)
    {
        std::thread t (&State::run <Function, Args...>, this, function, args...);
        t . join ();
    }

    template <class Function, class... Args>
    void
    run (Function&& function, Args&&... args)
    {
        std::cout << this -> message << std::endl;
        function (args...);
    }

    ~State () = default;

    std::string message;
};

void
print (std::string message)
{
    std::cout << message << std::endl;
}

int main (int argc, char ** argv)
{
    State state ("Hello, World!\n");

    state.spawn (print, "Goodbye, World!\n");

    return 0;
}

When I try to compile it using clang 6.0.1, I get the following output.

$ clang++ main.cpp
In file included from main.cpp:2:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:242:40: error: 
      no matching member function for call to '_M_invoke'
        -> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:127:8: note: 
      in instantiation of template class
      'std::thread::_Invoker<std::tuple<void (State::*)(void
      (&)(std::__cxx11::basic_string<char>), char const (&)[17]), State *,
      void (*)(std::__cxx11::basic_string<char>), const char *> >'
      requested here
              __make_invoker(std::forward<_Callable>(__f),
              ^
main.cpp:14:15: note: in instantiation of function template specialization
      'std::thread::thread<void (State::*)(void
      (&)(std::__cxx11::basic_string<char>), char const (&)[17]), State *,
      void (&)(std::__cxx11::basic_string<char>), char const (&)[17]>'
      requested here
                std::thread t (&State::run <Function, Args...>, thi...
                            ^
main.cpp:41:8: note: in instantiation of function template specialization
      'State::spawn<void (&)(std::__cxx11::basic_string<char>), char const
      (&)[17]>' requested here
        state.spawn (print, "Goodbye, World!\n");
              ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:231:4: note: 
      candidate template ignored: substitution failure [with _Ind = <0, 1,
      2, 3>]: no matching function for call to '__invoke'
          _M_invoke(_Index_tuple<_Ind...>)
          ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:186:13: error: 
      type 'std::thread::_Invoker<std::tuple<void (State::*)(void
      (&)(std::__cxx11::basic_string<char>), char const (&)[17]), State *,
      void (*)(std::__cxx11::basic_string<char>), const char *> >' does not
      provide a call operator
        _M_run() { _M_func(); }
                   ^~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:182:2: note: 
      in instantiation of member function
      'std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
      (State::*)(void (&)(std::__cxx11::basic_string<char>), char const
      (&)[17]), State *, void (*)(std::__cxx11::basic_string<char>), const
      char *> > >::_M_run' requested here
        _State_impl(_Callable&& __f) : _M_func(std::forward<_Callab...
        ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:197:24: note: 
      in instantiation of member function
      'std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
      (State::*)(void (&)(std::__cxx11::basic_string<char>), char const
      (&)[17]), State *, void (*)(std::__cxx11::basic_string<char>), const
      char *> > >::_State_impl' requested here
        return _State_ptr{new _Impl{std::forward<_Callable>(__f)}};
                              ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.2.0/../../../../include/c++/8.2.0/thread:126:25: note: 
      in instantiation of function template specialization
      'std::thread::_S_make_state<std::thread::_Invoker<std::tuple<void
      (State::*)(void (&)(std::__cxx11::basic_string<char>), char const
      (&)[17]), State *, void (*)(std::__cxx11::basic_string<char>), const
      char *> > >' requested here
        _M_start_thread(_S_make_state(
                        ^
main.cpp:14:15: note: in instantiation of function template specialization
      'std::thread::thread<void (State::*)(void
      (&)(std::__cxx11::basic_string<char>), char const (&)[17]), State *,
      void (&)(std::__cxx11::basic_string<char>), char const (&)[17]>'
      requested here
                std::thread t (&State::run <Function, Args...>, thi...
                            ^
main.cpp:41:8: note: in instantiation of function template specialization
      'State::spawn<void (&)(std::__cxx11::basic_string<char>), char const
      (&)[17]>' requested here
        state.spawn (print, "Goodbye, World!\n");
              ^
2 errors generated.

I can't tell what I am doing wrong. I can tell that the compiler doesn't seem to like the first argument that I am passing to the thread object, but I don't know why or how to fix it.

I've tried to change the function from a member function reference to a static member function reference (adding the struct pointer as an additional argument). This has no substantial effect on the output.

I've also tried compiling it with GCC, to see if it tells me anything interesting. It seems to have the same problem with the code that clang does, but the output is more verbose and equally unenlightening to me.

Why does my program not compile? What is this error message trying to tell me?

Zistack
  • 516
  • 3
  • 10
  • it definetely has to do with how you are using references, especially in the explicit template – bolov Aug 21 '18 at 02:50

1 Answers1

1

You are getting this error because std::thread decay-copies the arguments given to its constructor before calling the initial function. You are passing in a string literal with 16 characters plus a terminating null---that is, an lvalue of type const char[17]. The decay-copy of this expression produces a prvalue of type const char* (the usual array-to-pointer conversion). However, you are telling std::thread to use your run function still expecting a const char (&)[17] argument. This array-to-pointer decay is irreversible so you cannot get the required lvalue back from std::thread's decayed copy.

Here is an example of how a snippet of your code might be written to compile:

template <class Function, class... Args>
void
spawn (Function&& function, Args&&... args)
{
    std::thread t (&State::run <std::decay_t<Function>, std::decay_t<Args>...>, this, std::forward<Function>(function), std::forward<Args>(args)...);
    t . join ();
}

template <class Function, class... Args>
void
run (Function function, Args... args)
{
    std::cout << this -> message << std::endl;
    function(args...);
}

Here, we are instantiating State::run with the decayed types, ensuring that we are passing a function with the appropriate signature to receive the decay-copied arguments that will be given by std::thread.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I actually sort of had a guess at this after reading [this question](https://stackoverflow.com/questions/9641960/c11-make-pair-with-specified-template-parameters-doesnt-compile). Thanks for spelling it out for me though. I could not have done that myself. – Zistack Aug 21 '18 at 03:13
  • OK, but the program also compiles without the `decay_t` and `forward` annotations on the template arguments in `spawn ()`, as long as I remove the rvalue reference annotations from `spawn ()`. Why is that? It seems like it shouldn't, given your explanation. – Zistack Aug 21 '18 at 03:18
  • @Zistack I can't explain here, but post another question and maybe I'll answer it – Brian Bi Aug 21 '18 at 17:13