如何将一个任意数量的输入输出参数旋转到一个函数?

问题描述

Input: 1,2,3,4

Output: 2,4,1

我的解决方案: play with my code

template <typename T,typename ... Param>
void rotate(T* first,Param* ... params) {
    std::vector<T*> tmp = {first,params...};
    if (tmp.size() <= 1) {return;}
    T f = *first;
    for (size_t i = 1; i < tmp.size(); ++i) {
        *tmp.at(i - 1) = *tmp.at(i);
    }
    *tmp.at(tmp.size() - 1) = f;
}

我想旋转任意数量的元素,如上所述。我的解决方案似乎有效,但是在我看来,这不是很“优雅”。我不喜欢在这里初始化向量。没有向量,有没有办法完成同一件事?也许有递归?

理想情况下,我还想传递引用而不是指针。

解决方法

这是一个不使用std::vector的不正确解决方案,其中所有参数都是通过引用传递的,并且只需要复制一个元素:

// THIS IS WRONG,SEE EDIT BELOW

template<typename T,typename ...Ts>
void rotate(T& first,Ts& ...rest) 
{
   auto first_copy = first; 
   std::tie(first,rest...) = {rest...,first_copy};
}

这里是demo


编辑:上面的解决方案很优雅,但是不正确,因为似乎未指定std::tuple成员的分配顺序。上面的代码依赖于std::tie的参数分配,是从左到右完成的,因此该解决方案不起作用。

这是使用std::apply的更详细的解决方案,可以保证按顺序调用传入的元组的参数:

template<typename T,Ts& ...rest) 
{
    auto first_copy = first;

    std::apply([&](auto&... lhs) {
        std::apply([&](auto&... rhs) {
            ((lhs = std::move(rhs)),...); 
        },std::tuple<T&,Ts&...>{rest...,first_copy});
    },Ts&...>{first,rest...});
}

虽然这比较冗长,不像第一个解决方案执行1个复制构造和N个复制分配,但此解决方案的优点是仅执行1个复制构造和N个移动分配。据我所知,第一种解决方案是不可能的。显然,这是正确的,这也是一大优势:)

这是demo,还显示了所做的复制/移动。


这是@ max66提供的甚至更简单的解决方案,它与std::apply的解决方案同样有效:

template<typename T,Ts& ...rest) 
{
  T first_copy{first}; 

  [&](auto& first_ref,auto & ... rest_ref) { 
      first = std::move(first_ref);
      (...,(rest = std::move(rest_ref))); 
  } (rest...,first_copy);
}

这里是demo

,

不使用std::vector但使用std::reference_wrapper的模板折叠的另一种用法(因此需要C ++ 17或更高版本)

template <typename H,typename ... Ts>
void rotate (H & head,Ts & ... tail) {
    H h0 { head };
    std::reference_wrapper<H>  rw { head };

    ( (rw.get() = tail,rw = tail),...,(rw.get() = h0) );
}

在C ++ 11 / C ++ 14中,您可以模拟模板折叠以初始化C样式的数组

template <typename H,Ts & ... tail) {
    using unused = int[];
    H h0 { head };
    std::reference_wrapper<H>  rw { head };
    
    (void)unused { 0,(rw.get() = tail,rw = tail,0)... };

    rw.get() = h0;
}
,

如果可以使用C ++ 17,那么如何使用如下模板折叠呢?

template <typename T,typename ... Param>
void rotate(T* first,Param* ... params) {
    std::array<T,1u+sizeof...(params)> tmp = {*params...,*first};

    std::size_t  index {};

    ( (*first = tmp[index++]),(*params = tmp[index++]) );
}

也可以参考文献

template <typename T,typename ... Param>
void rotate(T & first,Param & ... params) {
    std::array<T,1u+sizeof...(params)> tmp = {params...,first};

    std::size_t  index {};

    ( (first = tmp[index++]),(params = tmp[index++]) );
}

但是您必须使用不同的名称(显然)

rotate(i,j,k,l,m,n);

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...