我正在尝试创建一个可变参数模板函数,该函数可以将函数调用到连续的参数对。
所需的功能签名是:
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);
在我的上下文中,操作顺序并不真正相关。该函数必须能够修改对象left
和right
,因此通过引用传递。我不能简单地通过基类指针使用多态访问,因为对象具有不同的类模板,共享类不能真正被取出。
是否可以通过可变参数模板函数实现这样的调用序列?我见过的包扩展和折叠表达式示例似乎都没有涵盖这种情况。或者我应该以不同的方式传递我的对象?
我的初始尝试,包含在下面(省略了一些细节),将所有模板参数打包到一个元组中,然后使用'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;
}
答案 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
要使其符合c ++ 11,需要使用available integer_sequence implementations之一。