我实际上正在考虑类似于python中的'*'运算符,如下所示:
args = [1,2,4]
f(*args)
C ++中是否有类似的解决方案?
我能想到的是:
template <size_t num_args, typename FuncType>
struct unpack_caller;
template <typename FuncType>
struct unpack_caller<3>
{
void operator () (FuncType &f, std::vector<int> &args){
f(args[0], args[1], args[3])
}
};
上面我假设只有int
参数类型。
问题在于我觉得将unpack_caller的所有特化都写成num_args
的不同值是一件麻烦事。
对此有什么好的解决方案吗?感谢。
答案 0 :(得分:7)
您可以使用pack of indices:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
void call(FuncType &f, std::vector<int> &args, indices<I...>){
f(args[I]...);
}
public:
template <typename FuncType>
void operator () (FuncType &f, std::vector<int> &args){
assert(args.size() == num_args); // just to be sure
call(f, args, BuildIndices<num_args>{});
}
};
但是没有办法在模板中删除指定大小的需要,因为向量的大小是运行时构造,我们需要在编译时使用大小。
答案 1 :(得分:2)
更新为@Fernandes的答案。
是的,确实有一种方法可以消除在模板参数中指定num_args
的需要。这是因为num_args
是由函数签名而不是向量确定的。在运行时应检查的是向量的大小和函数的Arity。
请参见以下示例。
#include <iostream>
#include <utility>
#include <vector>
#include <cassert>
namespace util {
template <typename ReturnType, typename... Args>
struct function_traits_defs {
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
template <size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename T>
struct function_traits_impl;
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
template <size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <size_t N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <size_t N>
using BuildIndices = typename build_indices<N>::type;
namespace details {
template <typename FuncType,
typename VecType,
size_t... I,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT do_call(FuncType& func,
VecType& args,
indices<I...> ) {
assert(args.size() >= Traits::arity);
return func(args[I]...);
}
} // namespace details
template <typename FuncType,
typename VecType,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT unpack_caller(FuncType& func,
VecType& args) {
return details::do_call(func, args, BuildIndices<Traits::arity>());
}
} // namespace util
int func(int a, int b, int c) {
return a + b + c;
}
int main() {
std::vector<int> args = {1, 2, 3};
int j = util::unpack_caller(func, args);
std::cout << j << std::endl;
return 0;
}