【问题标题】:Any Solution to Unpack a Vector to Function Arguments in C++?在 C++ 中将向量解包为函数参数的任何解决方案?
【发布时间】:2012-06-15 04:14:51
【问题描述】:

我实际上正在考虑类似于 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 参数类型。

问题是我觉得为 num_args 的不同值编写 unpack_caller 的所有特化很麻烦。

有什么好的解决办法吗?谢谢。

【问题讨论】:

  • 我怀疑答案是否定的。函数的参数数量是编译时构造,而向量中的元素数量是运行时构造。我无法证明这是不可能的,但我强烈怀疑这是不可能的。
  • templatetypedef:当然有可能。请参阅@R 给出的解决方案。马蒂尼奥·费尔南德斯。
  • @templatetypedef 虽然向量的大小是一个运行时变量,但函数的参数个数是一个编译时常数。这就是为什么它是可能的。我们可以通过boost::function_traits&lt;f&gt;::arity获取它
  • @DanqiWang- 但是,如果您允许可变参数函数(使用可变参数模板或使用可变参数),下面列出的解决方案将不起作用。这就是我最初表达的担忧。
  • @templatetypedef 我同意。我没有考虑到这一点。好点子。

标签: c++


【解决方案1】:

您可以使用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>{});
    }
};

虽然没有办法消除在模板中指定大小的需要,因为向量的大小是一个运行时构造,我们需要在编译时设置大小。

【讨论】:

  • 我认为assert 应该使用&gt;= 而不是==,如assert(args.size() &gt;= num_args);。它更灵活!
  • @Fernandes 这正是我想要的。
  • @Fernandes 我只有g++-4.5,不支持模板别名,所以我改用typedef。在operator () 我必须做typedef typename build_indices&lt;num_args&gt;::type idx_type; call(f, args, idx_type{});。有更好的解决方案吗?
  • @Fernandes 实际上可以通过在opertor () 中使用boost::function_traits&lt;FuncType&gt;::arity 来删除模板参数num_args。但我阅读了boost::function_traits 的实现,似乎它最多只支持11 个参数。我认为值得保留num_args 使其通用。
【解决方案2】:

更新@Fernandes 的回答。

是的,确实有一种方法可以消除在模板参数中指定num_args 的需要。这是因为num_args 是由函数签名决定的,而不是向量。在运行时应该检查的是向量的大小和函数的数量。

请看下面的例子。

#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;
}

【讨论】:

    猜你喜欢
    • 2023-02-21
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多