C++ 控制多个变量参数包的解包,每个变量包对应一个更奇特的元组 背景/动机

C++ 控制多个变量参数包的解包,每个变量包对应一个更奇特的元组 背景/动机,c++,templates,variadic-templates,template-meta-programming,sfinae,C++,Templates,Variadic Templates,Template Meta Programming,Sfinae,我一直在玩VC++2015,研究如何编写实用程序来处理元组和其他可变位和片段 我感兴趣的第一个函数是common或garden tuple_for_all函数。对于函数f和元组t依次调用f(get(t),f(get(t),依此类推 到目前为止,一切都很简单 template<typename Tuple, typename Function, std::size_t... Indices> constexpr void tuple_for_each_aux(Function&

我一直在玩VC++2015,研究如何编写实用程序来处理元组和其他可变位和片段

我感兴趣的第一个函数是common或garden tuple_for_all函数。对于函数
f
和元组
t
依次调用
f(get(t)
f(get(t)
,依此类推

到目前为止,一切都很简单

template<typename Tuple, typename Function, std::size_t... Indices>
constexpr void tuple_for_each_aux(Function&& f, Tuple&& t, std::index_sequence<Indices...>) {
    using swallow = int[];
    static_cast<void>(swallow{ 0, (std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t))), void(), 0)... });
}

template<typename Function, typename Tuple>
constexpr void tuple_for_each(Function&& f, Tuple&& t) {
    return tuple_for_each_aux(std::forward<Function>(f), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
<>这对于函数返回值非常有用,但是由于Value/Cuth>是令人讨厌的退化,我们不能做一个<代码> STD::tuple < /代码>,它对于Value>代码>返回函数是不起作用的。我们不能直接返回类型超载,但是C++给了我们处理这个的工具,Sfaya:< /P>
template<typename Function, typename Tuple, std::size_t... Indices, typename = std::enable_if_t<std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuple>>)>>::value>>
constexpr void tuple_for_each_aux(Function&& f, Tuple&& t, std::index_sequence<Indices...>) {
    using swallow = int[];
    static_cast<void>(swallow{ 0, (std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t))), void(), 0)... });
}

template<typename Function, typename Tuple, std::size_t... Indices, typename = std::enable_if_t<!std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuple>>)>>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, Tuple&& t, std::index_sequence<Indices...>) {
    return std::make_tuple(std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t)))...);
}

template<typename Function, typename Tuple>
constexpr decltype(auto) tuple_for_each(Function&& f, Tuple&& t) {
    return tuple_for_each_aux(std::forward<Function>(f), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
(顺便说一句,VC++2015现在似乎被窃听了;甚至对于带括号的初始值设定项也是如此,因为优化器团队)

我更感兴趣的是
std::enable_if_t
检查。我们不检查函数是否为元组中的每种类型返回non-
void
,只返回第一种类型。但实际上,它应该是all或nothing。
all\u true
技术为我们解决了这一问题:

template <bool...> struct bool_pack;

template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template<typename Function, typename Tuple, std::size_t... Indices, typename = std::enable_if_t<all_true<std::is_void<std::result_of_t<Function(std::tuple_element_t<Indices, std::decay_t<Tuple>>)>>::value...>::value>>
constexpr void tuple_for_each_aux(Function&& f, Tuple&& t, std::index_sequence<Indices...>)
{
    using swallow = int[];
    static_cast<void>(swallow{ 0, (std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t))), void(), 0)... });
}

template<typename Function, typename Tuple, std::size_t... Indices, typename = std::enable_if_t<!std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuple>>)>>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, Tuple&& t, std::index_sequence<Indices...>)
{
    return decltype(std::make_tuple(std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t)))...)){std::forward<Function>(f)(std::get<Indices>(std::forward<Tuple>(t))) ...};
}

template<typename Function, typename Tuple>
constexpr decltype(auto) tuple_for_each(Function&& f, Tuple&& t)
{
    return tuple_for_each_aux(std::forward<Function>(f), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
<天真>,我们希望第一个<代码>…>代码>扩展<代码> Tuples < /代码>,第二个<代码>…>代码>扩展<代码>索引< /代码>。但是参数包扩展不提供这种控制。如果<<代码>…> />代码中包含的表达式包含多个参数包,则<代码>…>代码>尝试将它们全部打包成PAR。allel(VC++;它发出一个编译器错误,它们的长度不同),或者根本找不到参数包(g++;它发出一个编译器错误,没有包)

幸运的是,这种情况可以通过额外的间接层来处理,以分离扩展:

template <bool...> struct bool_pack;

template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template<size_t N, typename Function, typename... Tuples, typename = std::enable_if_t<std::is_void<std::result_of_t<Function(std::tuple_element_t<N, std::decay_t<Tuples>>...)>>::value>>
constexpr void tuple_for_each_aux(Function&& f, Tuples&&... ts)
{
    return std::forward<Function>(f)(std::get<N>(std::forward<Tuples>(ts))...);
}

template<typename Function, typename... Tuples, std::size_t... Indices, typename = std::enable_if_t<all_true<std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuples>>...)>>::value>::value>>
constexpr void tuple_for_each_aux(Function&& f, std::index_sequence<Indices...>, Tuples&&... ts)
{
    using swallow = int[];
    static_cast<void>(swallow{ 0, (tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...), void(), 0)... });
}

template<std::size_t N, typename Function, typename... Tuples, typename = std::enable_if_t<!std::is_void<std::result_of_t<Function(std::tuple_element_t<N, std::decay_t<Tuples>>...)>>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, Tuples&&... ts)
{
    return std::forward<Function>(f)(std::get<N>(std::forward<Tuples>(ts))...);
}

template<typename Function, typename... Tuples, std::size_t... Indices, typename = std::enable_if_t<all_true<!std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuples>>...)>>::value>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, std::index_sequence<Indices...>, Tuples&&... ts)
{
    return decltype(std::make_tuple(tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...)...)) { tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...)... };
}

template<typename Function, typename Tuple, typename... Tuples>
constexpr decltype(auto) tuple_for_each(Function&& f, Tuple&& t, Tuples&&... ts)
{
    return tuple_for_each_aux(std::forward<Function>(f), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}, std::forward<Tuple>(t), std::forward<Tuples>(ts)...);
}
functor
对象需要强制使用
void
路径,因为第三个元组元素上的求值函数返回
void
。但是我们的启用检查只查看第一个元素。而且由于失败发生在SFINAE驱动的“重载”解析之后,SFINAE无法将我们保存在这里

但是同样,我们不能双重解包
enable\u if\t
表达式,原因与调用函数时无法解包相同:参数包扩展变得混乱,并试图同时迭代这两个表达式

这就是我要解决的问题。我需要一个在道德上等同于调用函数的间接方法,但我无法立即看到如何编写这个间接方法,使它真正起作用

有什么建议吗?

类型持有者:

template<class...> class typelist {};
现在要计算结果类型列表:

template<class F, std::size_t...Is, class... Tuples>
typelist<apply_result_type<F, Is, Tuples...>...> 
        compute_result_types(typelist<F, Tuples...>, std::index_sequence<Is...>);

template<class F, std::size_t Size, class... Tuples>
using result_types = decltype(compute_result_types(typelist<F, Tuples...>(),
                                                   std::make_index_sequence<Size>()));
最后是实际的SFINAE(仅显示一个):

模板{}>>
每个辅助函数的constexpr void元组(函数和函数,标准::索引序列,
元组(&…ts)
{
使用swallow=int[];
静态转换(swallow{0,(tuple_表示每个辅助(std::forward(f),std::forward(ts)…),void(),0)…);
}

.

每个aux(函数和函数、元组和…ts)只需要一个版本的
tuple\u;
void f();void/*或decltype(auto)*/g(){return f();}
没问题。哦,好地方,增量更改的危险。当我挂起
enable\u if\u t
检查时,它更有意义。嗯,在g++中看起来不错。不幸的是,它已经超出了VC++的能力极限,尽管我没有立即看到它出错的原因。
template <bool...> struct bool_pack;

template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template<size_t N, typename Function, typename... Tuples, typename = std::enable_if_t<std::is_void<std::result_of_t<Function(std::tuple_element_t<N, std::decay_t<Tuples>>...)>>::value>>
constexpr void tuple_for_each_aux(Function&& f, Tuples&&... ts)
{
    return std::forward<Function>(f)(std::get<N>(std::forward<Tuples>(ts))...);
}

template<typename Function, typename... Tuples, std::size_t... Indices, typename = std::enable_if_t<all_true<std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuples>>...)>>::value>::value>>
constexpr void tuple_for_each_aux(Function&& f, std::index_sequence<Indices...>, Tuples&&... ts)
{
    using swallow = int[];
    static_cast<void>(swallow{ 0, (tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...), void(), 0)... });
}

template<std::size_t N, typename Function, typename... Tuples, typename = std::enable_if_t<!std::is_void<std::result_of_t<Function(std::tuple_element_t<N, std::decay_t<Tuples>>...)>>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, Tuples&&... ts)
{
    return std::forward<Function>(f)(std::get<N>(std::forward<Tuples>(ts))...);
}

template<typename Function, typename... Tuples, std::size_t... Indices, typename = std::enable_if_t<all_true<!std::is_void<std::result_of_t<Function(std::tuple_element_t<0, std::decay_t<Tuples>>...)>>::value>::value>>
constexpr decltype(auto) tuple_for_each_aux(Function&& f, std::index_sequence<Indices...>, Tuples&&... ts)
{
    return decltype(std::make_tuple(tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...)...)) { tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...)... };
}

template<typename Function, typename Tuple, typename... Tuples>
constexpr decltype(auto) tuple_for_each(Function&& f, Tuple&& t, Tuples&&... ts)
{
    return tuple_for_each_aux(std::forward<Function>(f), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}, std::forward<Tuple>(t), std::forward<Tuples>(ts)...);
}
struct functor
{
    int operator()(int a, int b) { return a + b; }
    double operator()(double a, double b) { return a + b; }
    void operator()(char, char) { return;  }
};

int main()
{
    auto t1 = std::make_tuple(1, 2.0, 'a');
    auto t2 = std::make_tuple(2, 4.0, 'b');
    tuple_for_each(functor{}, t1, t2);
    return 0;
}
template<class...> class typelist {};
template<class F, std::size_t I, class...Tuples>
using apply_result_type = decltype(std::declval<F>()(std::get<I>(std::declval<Tuples>())...));
template<class F, std::size_t...Is, class... Tuples>
typelist<apply_result_type<F, Is, Tuples...>...> 
        compute_result_types(typelist<F, Tuples...>, std::index_sequence<Is...>);

template<class F, std::size_t Size, class... Tuples>
using result_types = decltype(compute_result_types(typelist<F, Tuples...>(),
                                                   std::make_index_sequence<Size>()));
template<class... Ts>
all_true<!std::is_void<Ts>::value...> do_is_none_void(typelist<Ts...>);

template<class TL>
using has_no_void_in_list = decltype(do_is_none_void(TL()));
template<typename Function, typename... Tuples, std::size_t... Indices,
         typename = std::enable_if_t<!has_no_void_in_list<result_types<Function,
                                                                       sizeof...(Indices),
                                                                       Tuples...>>{}>>
constexpr void tuple_for_each_aux(Function&& f, std::index_sequence<Indices...>, 
                                  Tuples&&... ts)
{
    using swallow = int[];
    static_cast<void>(swallow{ 0, (tuple_for_each_aux<Indices>(std::forward<Function>(f), std::forward<Tuples>(ts)...), void(), 0)... });
}