向后可变参数模板

Backward variadic template

我想编写一个模板函数来执行此操作(伪代码):

shift(T& a,T& b,T& c,...,T& y,const T& z) {
  a = b;
  b = c;
  ...
  y = z;
}

我的尝试涉及将可变参数作为第一个参数,如下所示:

template<typename A, typename B>
inline void shift(A& a, const B& b) noexcept {
  a = b;
}
template<typename B, typename C, typename... AA>
inline void shift(AA&... aa, B& b, const C& c) noexcept {
  shift(aa...,b);
  shift(b,c);
}

为什么这不起作用?可变参数只能是最后一个参数吗?那有没有办法让最后一个引用常量呢?

我需要一个 const 案例参考:

double a, b, c;
shift(a,b,c,5);

编辑: 好的,这个特殊问题当然可以通过颠倒函数参数的顺序来解决:

template<typename A, typename B>
inline void shift(const B& b, A& a) noexcept {
  a = b;
}
template<typename C, typename B, typename... AA>
inline void shift(const C& c, B& b, AA&... aa) noexcept {
  shift(b,aa...);
  b = c;
}

但我仍然想知道语法是否允许将可变参数放在最后一个以外的任何地方以及在什么情况下?

正如下面的 T.C. 评论指出的那样,在您的第一个示例中 aa 处于非推导上下文中(14.8.2.5 p5 定义了非推导上下文,包括 "a function parameter pack that does not occur at the end of the parameter-declaration-list") 所以无法推导出参数包。

if syntax allows for placing variadic arguments in any place other then the last and under what circumstances?

除了上述限制导致您的第一个示例出现问题外,模板参数包必须是最后一个模板参数,除非可以推导出所有后续模板参数。 14.1 [temp.param]p11:

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (14.8.2).

所以这没问题,因为两个模板参数包都可以独立于函数参数推导出来:

template<typename... T, typename... U>
  void f(std::tuple<T...>, std::tuple<U...>)
  { }

另一种编写 shift 函数的方法是使用 index_sequence

#include <tuple>
#include <iostream>

template<typename... T, size_t... I>
void
shift_impl(std::tuple<T...> t, std::index_sequence<I...>)
{
  // Use pack expansion with comma operator to populate unused array:
  int dummy[] = {
    (std::get<I>(t) = std::get<I+1>(t), 0)...
  };
}

template<typename T0, typename... T>
void
shift(T0&& arg0, T&&... args)
{
  shift_impl(std::tie(arg0, args...), std::index_sequence_for<T...>{});
}

int main()
{
  int i = 0, j = 1, k = 2;
  shift(i, j, k);
  std::cout << i << ' ' << j << ' ' << k << '\n';
}

[完全披露:我创作了 Hana]

我不是要回答你原来的问题,因为 Jonathan 做得很好。

但是,如果您正在寻找问题的解决方案并且可以使用无错误的 C++14 编译器(因此现在是 Clang),则可以使用 Boost.Hana。 Hana 是一个现代元编程库,使您能够在保持高级抽象的同时进行元编程。因此,您可以以可读的方式编写您需要的内容,而不必了解该语言的所有肮脏技巧。图书馆会为您解决这个问题:

#include <boost/hana.hpp>
#include <functional> // for std::ref
#include <iostream>
namespace hana = boost::hana;

template <typename T0, typename ...T>
void shift(T0&& t0, T&& ...ts) {
    auto args = hana::make_tuple(std::ref(t0), std::ref(ts)...);
    hana::for_each(hana::range_c<int, 0, sizeof...(ts)>, [&](auto i) {
        args[i].get() = args[i + hana::int_c<1>].get();
    });
}

int main() {
    int i = 0, j = 1, k = 2;
    shift(i, j, k);
    std::cout << i << ' ' << j << ' ' << k << '\n';
}