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

根据函数签名将变量模板中的类型作为值/引用转发

  •  3
  • definelicht  · 技术社区  · 7 年前

    这个问题涉及 this , this 而且可能 this .

    我有以下课程,其中 AddFunction 方法接收一个函数和该函数的参数列表,然后生成一个 std::thread 使用传递的参数调用传递的函数:

    class Processes {
    public:
      Processes() {}
    
      ~Processes() {
        for (auto &t : threads_) {
          t.join();
        }
      }
    
      template <class Function, typename... Args>
      void AddFunction(Function &&func, Args &&... args) {
        threads_.emplace_back(std::forward<Function>(func),
                              std::forward<Args>(args)...);
      }
    
    private:
      std::vector<std::thread> threads_;
    }
    

    这将导致每个参数都有一个副本,如果对象不可复制,编译将失败,因为 标准::螺纹 需要包装引用 std::ref 为了保证此对象将超过线程的生存期,否则将复制它。

    当在目标函数签名中指定时 .

    我试着使用lambda:

    template <class Function, typename... Args>
    void AddFunction(Function &&func, Args &&... args) {
      threads_.emplace_back([&]() { func(std::forward<Args>(args)...); });
    }
    

    但这会导致不正确的行为,因为lambda在按值传递值之前通过引用捕获值,从而导致按引用捕获行为。

    如何实现将参数作为值或引用转发的函数 根据目标函数签名 ?


    例子:

    void Foo(int a, std::vector<int> const &b) { /* ... */ }
    
    int main() {
      Processes procs;
      int a = 6;
      std::vector<int> b;
      procs.AddFunction(
        Foo,
        a, // Should be passed by value
        b  // Should be passed by reference (as implemented by std::ref)
      );
      return 0;
    }
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   Jarod42    7 年前

    您可以将函数签名更改为不那么通用:

    首先是一些助手:

    template <typename T> struct non_deducible { using type = T; };
    template <typename T> using non_deducible_t = typename non_deducible<T>::type;
    
    template <typename T>
    auto passed_by(T& t, std::true_type)
    {
        return std::ref(t);
    }
    
    template <typename T>
    T&& passed_by(T&& t, std::false_type)
    {
        return std::forward<T>(t);
    }
    

    然后

    template <class Ret, typename... Args>
    void AddFunction(Ret (*func)(Args...), non_deducible_t<Args>... args) {
        threads_.emplace_back(func,
                              passed_by(std::forward<Args>(args),
                                        std::is_reference<Args>{})...);
    }
    
        2
  •  2
  •   Vittorio Romeo    7 年前

    如果您想沿着lambda路线走下去,您可以实现一些实用程序 allow you to capture by "perfect-forward" -这意味着 R值 被移入封盖 左值 通过引用捕获。您可以使用 std::tuple<T> 到任一商店 T T& (我的链接文章有一个更简洁的实现) :

    template <class Function, typename... Args>
    void AddFunction(Function &&func, Args &&... args) 
    {
        threads_.emplace_back([
            targs = std::tuple<Args...>{std::forward<Args>(args)...},
            tfunc = std::tuple<Function>(func)]() mutable
        { 
            std::apply([&targs](auto&& x_func)
            {
                std::apply([&x_func](auto&&... x_args)
                { 
                    std::forward<Function>(x_func)(
                        std::forward<Args>(x_args)...
                    );
                }, targs);
            }, tfunc);
        });
    }
    

    live wandbox example