"解包"std :: array <T,N>作为函数的参数

The*_*Ham 3 c++ templates c++11 stdtuple stdarray

这是一个非常好的(不是我的)示例,你可以如何扩展(或"爆炸")元组作为函数的参数:

template<int ...I> struct index_tuple_type {
  template<int N> using append = index_tuple_type<I..., N>;
};

template<int N> struct make_index_impl {
  using type = typename make_index_impl<N-1>::type::template append<N-1>;
};

template<> struct make_index_impl<0> { using type = index_tuple_type<>; };

template<int N> using index_tuple = typename make_index_impl<N>::type;

template <typename I, typename ...Args>
struct func_traits;

template <typename R, int ...I, typename ...Args>
struct func_traits<R, index_tuple_type<I...>, Args...> {
  template <typename TT, typename FT>
  static inline R call(TT &&t, FT &&f) {
    return f(std::get<I>(std::forward<TT>(t))...);
  }
};

template<
  typename FT,
  typename ...Args, 
  typename R = typename std::result_of<FT(Args&&...)>::type
>
inline R explode(std::tuple<Args...>& t, FT &&f) {
  return func_traits<R, index_tuple<sizeof...(Args)>, Args...>
    ::call(t, std::forward<FT>(f));
}
Run Code Online (Sandbox Code Playgroud)

然后你就可以这样使用:

void test1(int i, char c) {
  printf("%d %c\n", i, c);
}

int main() {
  std::tuple<int, char> t1{57, 'a'};
  explode(t1, test1);
}
Run Code Online (Sandbox Code Playgroud)

实时版

我在徘徊你怎么能做同样的事情,std::array因为它非常像元组.std::get<N>与之合作std::array我认为修改此解决方案很容易.但这样的事情不起作用:

template<
  typename FT,
  typename Arg,
  std::size_t I,
  typename R = typename std::result_of<FT(Arg&&)>::type
>
inline R explode(std::array<Arg, I>& t, FT &&f) {
  return func_traits<R, index_tuple<I>, Arg>::
    call(t, std::forward<FT>(f));
}

void test2(int i1, int i2) {
  printf("%d %d\n", i1, i2);
}

int main() {
  std::array<int, int> t1{1, 2};
  explode(t2, test1);
}
Run Code Online (Sandbox Code Playgroud)

因为这个部分std::result_of<FT(Arg&&)>::type.参数类型Arg&&错误且result_of没有字段type.对于元组Args&&...扩展,但现在它应该是"重复" I次.有没有办法这样做,result_of所以可以扣除返回的类型?

此外,我想知道,有工具来"解压" tuple,并array有可能为"解压"递归(可能使用enable_if)结构是怎样的tuple<array<int, 2>, tuple<array<double,3>, ...设计呢?某种树在哪里tuplearray哪些树枝,其他类型是树叶?

Yak*_*ont 8

// enable argument dependent lookup on `get` call:
namespace aux {
  using std::get;
  template<size_t N, class T>
  auto adl_get( T&& )->decltype( get<N>(std::declval<T>()) );
}
using aux::adl_get;
template<class F, class TupleLike, size_t...Is>
auto explode( F&& f, TupleLike&& tup, std::index_sequence<Is...> )
-> std::result_of_t< F( decltype(adl_get<Is>(std::forward<TupleLike>(tup)))... ) >
{
  using std::get; // ADL support
  return std::forward<F>(f)( get<Is>(std::forward<TupleLike>(tup))... );
}
Run Code Online (Sandbox Code Playgroud)

是第一步. std::index_sequence是C++ 14,但它很容易在C++ 11中实现.

接下来的步骤也很简单.

首先,一个traits类决定了哪些类型是元组.我会继续,只是鸭子类型使用它们,但我们将要使用的许多函数和特征类不是SFINAE友好的:

template<class T>
struct tuple_like:std::false_type{};
template<class... Ts>
struct tuple_like<std::tuple<Ts...>>:std::true_type{};
template<class... Ts>
struct tuple_like<std::pair<Ts...>>:std::true_type{};
template<class T, size_t N>
struct tuple_like<std::array<T,N>>:std::true_type{};
Run Code Online (Sandbox Code Playgroud)

接下来,重载explode仅适用于tuple_like类型:

template<class F, class TupleLike,
  class TupleType=std::decay_t<TupleLike>, // helper type
  class=std::enable_if_t<tuple_like<TupleType>{}>> // SFINAE tuple_like test
auto explode( F&& f, TupleLike&& tup )
-> decltype(
  explode(
    std::declval<F>(),
    std::declval<TupleLike>(), 
    std::make_index_sequence<std::tuple_size<TupleType>{}>{}
  )
)
{
   using indexes = std::make_index_sequence<std::tuple_size<TupleType>{}>;
   return explode(
     std::forward<F>(f),
     std::forward<TupleLike>(tup),
     indexes{}
   );
}
Run Code Online (Sandbox Code Playgroud)

如果你缺乏constexpr支持,你需要一些变化{}::value.

以上是成对,数组或元组的技巧.如果要添加对其他类似元组的类型的支持,只需添加一个特化tuple_like并确保std::tuple_size适合您的类型并且get<N>是ADL重载(在类型的封闭命名空间中).


std::make_index_sequence 也是C++ 14,但很容易用C++ 11编写.

template<size_t...>
struct index_sequence{};
namespace details {
  template<size_t count, size_t...Is>
  struct mis_helper:mis_helper<count-1, count-1, Is...> {};
  template<size_t...Is>
  struct mis_helper<0,Is...> {
    using type=index_sequence<Is...>;
  };
}
template<size_t count>
using make_index_sequence=typename details::mis_helper<count>::type;
Run Code Online (Sandbox Code Playgroud)

(对于C++ 14库来说,这是一个很差的QOI,它应该至少使用log下降,因为它需要O(n)模板递归模板实例化大小为n的列表.但是,n小于几百,没关系).

std::enable_if_t<?>是C++ 14,但在C++ 11中只是typename std::enable_if<?>::type.