代码之家  ›  专栏  ›  技术社区  ›  Holt 111111

函数类reduce函数中的转发和返回类型

  •  5
  • Holt 111111  · 技术社区  · 7 年前

    reduce 功能类似于 std::reduce

    template <typename F, typename T>
    constexpr decltype(auto) reduce(F&&, T &&t) {
        return std::forward<T>(t);
    }
    
    template <typename F, typename T1, typename T2, typename... Args>
    constexpr decltype(auto) reduce(F&& f, T1&& t1, T2&& t2, Args&&... args) {
        return reduce(
            std::forward<F>(f),
            std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)),
            std::forward<Args>(args)...);
    }
    

    std::vector<int> vec;
    decltype(auto) u = reduce([](auto &a, auto b) -> auto& {
            std::copy(std::begin(b), std::end(b), std::back_inserter(a));
            return a;
        }, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6});
    
    assert(&vec == &u); // ok
    assert(vec == std::vector<int>{1, 2, 3, 4, 5, 6}); // ok
    

    但以下方法不起作用:

    auto u = reduce([](auto a, auto b) {
            std::copy(std::begin(b), std::end(b), std::back_inserter(a));
            return a;
        }, std::vector<int>{}, std::set<int>{1, 2}, 
        std::list<int>{3, 4}, std::vector<int>{5, 6});
    

    这基本上崩溃了-为了使这一点起作用,我需要改变第一个定义 收件人:

    template <typename F, typename T>
    constexpr auto reduce(F&&, T &&t) {
        return t;
    }
    

    但是如果我这么做了,第一个片段就不再有效了。

    问题在于参数的转发和返回类型 减少

    减少

    3 回复  |  直到 7 年前
        1
  •  3
  •   Columbo    7 年前

    你可以试试

    template <typename F, typename T>
    constexpr T reduce(F&&, T &&t) {
        return std::forward<T>(t);
    }
    

    fine with it .

    或者,只需使用第二种变体并包装即可 vec std::ref ,进行必要的修改。这也是模板按值处理对象时的标准方法。

        2
  •  2
  •   Casey    7 年前

    问题案例中的lambda:

    [](auto a, auto b) {
        std::copy(std::begin(b), std::end(b), std::back_inserter(a));
        return a;
    }
    

    按值返回,因此当 reduce 递归:

     return reduce(
        std::forward<F>(f),
        std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)), // HERE
        std::forward<Args>(args)...);
    

    template <typename F, typename T>
    constexpr decltype(auto) reduce(F&&, T &&t) {
        return std::forward<T>(t);
    }
    

    它返回一个绑定到该临时对象的引用,该临时对象在展开递归时被破坏,因此 v

    最简单的修复方法是不在lambda中创建临时值,而是将结果累积到输入对象中,您知道输入对象至少会一直存在到完整表达式结束( DEMO

    auto fn = [](auto&& a, auto const& b) -> decltype(auto) {
        std::copy(std::begin(b), std::end(b), std::back_inserter(a));
        // Or better:
        // a.insert(std::end(a), std::begin(b), std::end(b));
        return static_cast<decltype(a)>(a);
    };
    
    std::vector<int> vec;
    decltype(auto) u = reduce(fn, vec,
        std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6});
    
    assert(&vec == &u); // ok
    assert((vec == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok
    
    auto v = reduce(fn, std::vector<int>{},
        std::set<int>{1, 2},  std::list<int>{3, 4}, std::vector<int>{5, 6});
    assert((v == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok
    
        3
  •  0
  •   Yakk - Adam Nevraumont    7 年前

    template<class F, class T=void>
    struct reduce_t;
    
    template<class F>
    reduce_t<F> reduce( F&& f );
    
    template<class F, class T>
    reduce_t<F, T> reduce( F&& f, T&& t );
    
    template<class F, class T>
    struct reduce_t {
      F f;
      T t;
      template<class Rhs>
      auto operator|( Rhs&& rhs )&&{
        return reduce( f, f( std::forward<T>(t), std::forward<Rhs>(rhs) ) );
      }
      T get()&&{ return std::forward<T>(t); }
    };
    template<class F>
    struct reduce_t<F,void> {
      F f;
      template<class Rhs>
      auto operator|( Rhs&& rhs )&&{
        return reduce( f, std::forward<Rhs>(rhs) );
      }
    };
    
    template<class F>
    reduce_t<F> reduce( F&& f ) {
      return {std::forward<F>(f)};
    }
    
    template<class F, class T>
    reduce_t<F, T> reduce( F&& f, T&& t ) {
      return {std::forward<F>(f), std::forward<T>(t)};
    }
    template<class F, class T, class...Ts>
    auto reduce( F&& f, T&& t, Ts&&...ts ) {
        return (reduce( std::forward<F>(f), std::forward<T>(t) ) | ... |  std::forward<Ts>(ts));
    }
    

    然后这些工作中的任何一项:

    decltype(auto) u = (reduce([](auto &a, auto b) -> auto& {
        std::copy(std::begin(b), std::end(b), std::back_inserter(a));
        return a;
    }) | vec | std::set<int>{1, 2} | std::list<int>{3, 4} | std::vector<int>{5, 6}).get();
    
    decltype(auto) u = reduce([](auto &a, auto b) -> auto& {
        std::copy(std::begin(b), std::end(b), std::back_inserter(a));
        return a;
    }, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}).get();
    
    auto u_val = (
        reduce([](auto a, auto b) {
          std::copy(std::begin(b), std::end(b), std::back_inserter(a));
          return a;
        })
        | std::vector<int>{} | std::set<int>{1, 2}
        | std::list<int>{3, 4} | std::vector<int>{5, 6}
    ).get();
    

    Live example .