问题描述
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);