Here's an INCORRECT solution without using std::vector, where all arguments are passed by reference, and only a single element needs to be copied:
// 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};
}
Here's a demo.
Edit: The above solution is elegant, but is incorrect, since it appears that the order of assignments to std::tuple members is unspecified. The code above relies on the assignments to the arguments to std::tie to be done from left to right, so the solution doesn't work.
Here's a more verbose solution using std::apply, which is guaranteed to invoke the arguments of the passed in tuple in order:
template<typename T, typename ...Ts>
void rotate(T& first, 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});
}, std::tuple<T&, Ts&...>{first, rest...});
}
While this is more verbose, unlike the first solution which does 1 copy-construction and N copy-assignments, this solution has the advantage that it only does 1 copy-construction and N move-assignments. This is not possible with the first solution, as far as I can tell. And obviously, it's correct, which is a big advantage too :)
Here's a demo that also shows the copies/moves made.
Here's an even simpler solution given by @max66, which is also as efficient as the solution with std::apply:
template<typename T, typename ...Ts>
void rotate(T& first, 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);
}
Here's a demo.