代码之家  ›  专栏  ›  技术社区  ›  Zistack

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

  •  0
  • Zistack  · 技术社区  · 8 年前

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

    #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的代码有同样的问题,但是输出对我来说更冗长,也同样不轻松。

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

    1 回复  |  直到 8 年前
        1
  •  1
  •   Brian Bi    8 年前

    您收到此错误是因为 std::thread decay在调用初始函数之前复制给其构造函数的参数。您正在传入一个字符串文字,其中包含16个字符和一个终止的空值,即类型为的左值 const char[17] . 此表达式的衰减副本生成类型为的prvalue const char* (通常的数组到指针转换)。但是,你告诉我 STD::线程 使用你的 run 函数仍应为 const char (&)[17] 争论。此数组到指针的衰减是不可逆的,因此无法从 STD::线程 已腐烂的副本。

    下面是如何编写代码片段进行编译的示例:

    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::线程 .