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

时间:2018-06-15 13:33:40

标签: c++ c++11 templates variadic-templates

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

所需的功能签名是:

template <typename ...Ts>
void apply(Ts &...args);

使用apply(t1, t2, t3)进行调用时,该函数应该生成一系列调用func(t1, t2)func(t2, t3),其中func是一个带签名的函数:

template <typename L, typename R>
void func(L &left, R &right);

在我的上下文中,操作顺序并不真正相关。该函数必须能够修改对象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;
}

2 个答案:

答案 0 :(得分: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...);
}

或者,更确切地说,它应该看起来像(感谢@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)...);
}

答案 1 :(得分:2)

另一种(c ++ 14)方法:

#include <utility>
#include <utility>
#include <tuple>
#include <iostream>

template <typename L, typename R>
void func(L &left, R &right) {
    std::cout << left << " " << right << std::endl;
}

template <typename Tup, std::size_t... Is>
void apply_impl(Tup&& tup, std::index_sequence<Is...>) {
    int dummy[] = { 0,  (static_cast<void>(func(std::get<Is>(tup), std::get<Is + 1>(tup))), 0)... };
    static_cast<void>(dummy);
}

template <typename ...Ts>
void apply(Ts &...args) {
    apply_impl(std::forward_as_tuple(args...), std::make_index_sequence<sizeof...(Ts) - 1>{});
}

int main() {
   int arr[] = {0, 1, 2, 3};
   apply(arr[0], arr[1], arr[2], arr[3]);
}

输出:

0 1
1 2
2 3

[online example]

要使其符合c ++ 11,需要使用available integer_sequence implementations之一。