线程构造函数周围的 Variadic 模板包装器无法编译

Variadic template wrapper around thread constructor fails to compile

我正在尝试编译以下程序。

#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;
}

当我尝试使用 clang 6.0.1 编译它时,我得到以下输出。

$ 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.

我不知道我做错了什么。我可以看出编译器似乎不喜欢我传递给线程对象的第一个参数,但我不知道为什么或如何修复它。

我试图将函数从成员函数引用更改为静态成员函数引用(添加结构指针作为附加参数)。这对输出没有实质影响。

我也试过用 GCC 编译它,看看它是否告诉我任何有趣的事情。它似乎与 clang 的代码有同样的问题,但输出更冗长并且对我来说同样没有启发性。

为什么我的程序无法编译?此错误消息试图告诉我什么?

您收到此错误是因为 std::thread 在调用初始函数之前衰减复制了提供给其构造函数的参数。您正在传递一个包含 16 个字符和一个终止空值的字符串文字——即 const char[17] 类型的左值。此表达式的衰减副本产生 const char* 类型的纯右值(通常的数组到指针转换)。但是,您告诉 std::thread 使用您的 run 函数仍然需要 const char (&)[17] 参数。这种数组到指针的衰减是不可逆的,因此您无法从 std::thread 的衰减副本中取回所需的左值。

下面是一个示例,说明如何编写代码片段进行编译:

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...);
}

在这里,我们用衰减类型实例化 State::run,确保我们传递的函数具有适当的签名以接收将由 std::thread 给出的衰减复制参数。