模板包扩展将函数应用于连续的参数对

nur*_*rbo 2 c++ templates variadic-templates c++11

我正在尝试创建一个可变参数模板函数,它将函数调用到连续的参数对.

所需的功能签名是:

template <typename ...Ts>
void apply(Ts &...args);
Run Code Online (Sandbox Code Playgroud)

当所谓apply(t1, t2, t3)的函数应该拨打电话的顺序func(t1, t2)func(t2, t3),其中func是有特色的功能:

template <typename L, typename R>
void func(L &left, R &right);
Run Code Online (Sandbox Code Playgroud)

在我的上下文中,操作顺序并不真正相关.该函数具有以能够修改对象leftright,因此通过引用传递.我不能简单地通过基类指针使用多态访问,因为对象具有不同的类模板,共享类不能真正被取出.

是否有可能通过可变参数模板函数实现这样的调用序列?我见过的包扩展和折叠表达示例似乎都没有涵盖这种情况.或者我应该以不同的方式传递我的物体?

我的初始尝试,包括在下面(省略了一些细节),将所有模板参数打包到一个元组中,然后使用'const for-loop'来"循环"通过元组元素.但是,我很快意识到这种方法不起作用,因为const-for循环中的lambda会调用operator() const,因此无法修改传递的对象.

我使用的代码确实产生了所需的调用序列,但是对象没有被修改(set_something()不是const函数).我不得不求助于使用具有不同数量的模板参数的包装函数,并func手动调用.

template <std::size_t Begin, typename Callable, std::size_t... I>
constexpr void const_for_impl(Callable &&func, std::index_sequence<I...>) {
  (func(std::integral_constant<std::size_t, Begin + I>{}), ...);
}

template <std::size_t Begin, std::size_t End, typename Callable>
constexpr void const_for(Callable &&func) {
  const_for_impl<Begin>(std::forward<Callable>(func),
                        std::make_index_sequence<End - Begin>{});
};

template <typename... Ts>
void apply(Ts *... args) {
  auto tuple = std::make_tuple(std::forward<Ts>(args)...);

  const_for<0, sizeof...(args) - 1>(
      [&](auto I) { func((std::get<I>(tuple)), (std::get<I + 1>(tuple))); });
};

template <typename L, typename R>
void func(L &l, R &r) {
  // Validate with some type traits
  static_assert(has_some_property<L>::value);
  static_assert(has_another_property<R>::value);

  // Get a shared pointer to something common
  auto common = std::make_shared<typename something_common<L, R>::type>();

  l.set_something(common);
  r.set_something(common);
};

// Application scenario
int main() {

  ComplexObjectA<SomeType, SomeParameter> a;
  ComplexObjectB<AnotherType, AnotherParameter> b;
  ComplexObjectC c;

  apply(a, b, c);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

bip*_*pll 5

所以有什么问题?简单的折叠式模板(并记住模板模式匹配相反!)

template<typename T1, typename T2>
void apply(T1 &&t1, T2 &&t2) { func(t1, t2); }

template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
    func(t1, t2);
    return apply(t2, ts...);
}
Run Code Online (Sandbox Code Playgroud)

或者,更准确地说,它应该看起来像(感谢@MaxLanghof):

void apply(T1 &&t1, T2 &&t2) {
    func(std::forward<T1>(t1), std::forward<T2>(t2));
}

template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
    func(std::forward<T1>(t1), t2);

    return apply(std::forward<T2>(t2), std::forward<TS>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)

  • @nurbo因为一般来说事先不知道(对我来说,至少),`func()`是否保持其第二个参数完好无损.如果它没有窃取其内容或以其他方式不可改变地改变它(你可以告诉它有'func`的源代码),那么你也可以`std :: forward <T2>(t2)`.通常,这次将它作为左值引用传递可能是安全的(它将在下一个递归步骤中转发). (2认同)