Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 将多个元组应用于同一函数(即“应用(f,元组…)”),而不使用递归或“元组”`_C++_Templates_Tuples_Variadic Templates_C++17 - Fatal编程技术网

C++ 将多个元组应用于同一函数(即“应用(f,元组…)”),而不使用递归或“元组”`

C++ 将多个元组应用于同一函数(即“应用(f,元组…)”),而不使用递归或“元组”`,c++,templates,tuples,variadic-templates,c++17,C++,Templates,Tuples,Variadic Templates,C++17,签名如下: template <class F, class Tuple> constexpr decltype(auto) apply(F&& f, Tuple&& t); 用法示例: std::tuple t0{1, 2, 3}; std::tuple t1{4, 5, 6}; auto sum = [](auto... xs){ return (0 + ... + xs); }; assert(multi_apply(sum, t0, t1)

签名如下:

template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t);
用法示例:

std::tuple t0{1, 2, 3};
std::tuple t1{4, 5, 6};
auto sum = [](auto... xs){ return (0 + ... + xs); };

assert(multi_apply(sum, t0, t1) == 1 + 2 + 3 + 4 + 5 + 6);

我可以想出各种简单的方法来实现
multi\u apply

  • 使用并调用
    std::experimental::apply

  • 使用递归将每个元组的参数绑定到一系列lambda,这些lambda最终调用原始函数

但我要问的是:我如何实现
multi\u apply
,而不诉诸
std::tuple\u cat
或递归?

理想情况下,我想做的是:为每个元组生成一个索引,并在相同的变量展开中将每个元组与其自己的索引序列进行匹配。这可能吗?例如:

// pseudocode-ish
template <class F, std::size_t... Idxs, class... Tuples>
constexpr decltype(auto) multi_apply_helper(
    F&& f, std::index_sequence<Idxs>... seqs,  Tuples&&... ts)
{
    return f(std::get<Idxs>(ts)...);
} 
//伪代码
模板
constexpr decltype(自动)多应用辅助程序(
F&&F,std::index_序列…seqs,tuple&…ts)
{
返回f(std::get(ts)…);
} 

以下是我的看法。它不使用递归,并在同一个包扩展中扩展这些元组,但需要做一些准备:

  • 我们为传入的元组构建一个引用元组,为右值参数构建右值引用,为左值参数构建左值引用,以便在最后的调用中进行适当的转发(正如T.C.在注释中指出的那样,这正是
    std::forward_as_tuple
    所做的)。元组是以右值的形式构建和传递的,因此引用折叠可以确保最后调用
    f
    时每个参数的值类别正确
  • 我们构建了两个扁平索引序列,其大小都等于所有元组大小之和:
    • 外部索引选择元组,因此它们重复相同的值(元组包中元组的索引),重复次数等于每个元组的大小
    • 内部元组选择每个元组中的元素,因此它们从
      0
      增加到比每个元组的元组大小小一个
一旦我们有了它,我们只需在调用
f
中展开两个索引序列

#include <tuple>
#include <array>
#include <cstddef>
#include <utility>
#include <type_traits>
#include <iostream>

template<std::size_t S, class... Ts> constexpr auto make_indices()
{
   constexpr std::size_t sizes[] = {std::tuple_size_v<std::remove_reference_t<Ts>>...};
   using arr_t = std::array<std::size_t, S>;
   std::pair<arr_t, arr_t> ret{};
   for(std::size_t c = 0, i = 0; i < sizeof...(Ts); ++i)
      for(std::size_t j = 0; j < sizes[i]; ++j, ++c)
      {
         ret.first[c] = i;
         ret.second[c] = j;
      }
   return ret;
}

template<class F, class... Tuples, std::size_t... OuterIs, std::size_t... InnerIs> 
constexpr decltype(auto) multi_apply_imp_2(std::index_sequence<OuterIs...>, std::index_sequence<InnerIs...>, 
                                           F&& f, std::tuple<Tuples...>&& t)
{
   return std::forward<F>(f)(std::get<InnerIs>(std::get<OuterIs>(std::move(t)))...);
}

template<class F, class... Tuples, std::size_t... Is> 
constexpr decltype(auto) multi_apply_imp_1(std::index_sequence<Is...>, 
                                           F&& f, std::tuple<Tuples...>&& t)
{
   constexpr auto indices = make_indices<sizeof...(Is), Tuples...>();
   return multi_apply_imp_2(std::index_sequence<indices.first[Is]...>{}, std::index_sequence<indices.second[Is]...>{},
      std::forward<F>(f), std::move(t));
}

template<class F, class... Tuples> 
constexpr decltype(auto) multi_apply(F&& f, Tuples&&... ts)
{
   constexpr std::size_t flat_s = (0U + ... + std::tuple_size_v<std::remove_reference_t<Tuples>>);
   if constexpr(flat_s != 0)
      return multi_apply_imp_1(std::make_index_sequence<flat_s>{}, 
         std::forward<F>(f), std::forward_as_tuple(std::forward<Tuples>(ts)...));
   else
      return std::forward<F>(f)();
}

int main()
{
   auto t0 = std::make_tuple(1, 2);
   auto t1 = std::make_tuple(3, 6, 4, 5);
   auto sum = [](auto... xs) { return (0 + ... + xs); };

   std::cout << multi_apply(sum, t0, t1, std::make_tuple(7)) << '\n';
}
其中,
sum\u array
可以是如下简单的内容

template<class T, std::size_t S> constexpr T sum_array(const T (& a)[S], std::size_t i = 0)
{
   return i < S ? a[i] + sum_array(a, i + 1) : 0;
}
实现可能会在
std::apply
上提供一个noexcept说明符(至少,libc++提供;libstdc++和MSVC目前不提供),因此可能也值得考虑。

替代版本:

template <class F, std::size_t... Is, class ... Ts>
constexpr decltype(auto) multiple_apply_impl(F&& f, std::index_sequence<Is...>, Ts&&... ts)
{
    constexpr auto p = [](){
        constexpr auto total_size = sizeof...(Is);
        std::array<std::size_t, total_size> outer{};
        std::array<std::size_t, total_size> inner{};
        std::size_t global_index = 0;
        std::size_t outer_value = 0;

        [[maybe_unused]] auto l = [&](std::size_t size)
        {
            for (std::size_t i = 0; i != size; ++i) {
                outer[global_index] = outer_value;
                inner[global_index] = i;
                ++global_index;
            }
            ++outer_value;
        };
        (l(std::tuple_size<std::decay_t<Ts>>::value), ...);

        return make_pair(outer, inner);
    }();
    [[maybe_unused]] constexpr auto outer = p.first;
    [[maybe_unused]] constexpr auto inner = p.second;

    using std::get;
    return std::invoke(std::forward<F>(f),
                       get<inner[Is]>(get<outer[Is]>(std::forward_as_tuple(std::forward<Ts>(ts)...)))...);
}

template <class F, class ... Ts>
constexpr decltype(auto) multiple_apply(F&& f, Ts&&... ts)
{
    constexpr auto total_size = (std::size_t{0} + ... + std::tuple_size<std::decay_t<Ts>>::value);

    return multiple_apply_impl(std::forward<F>(f),
                               std::make_index_sequence<total_size>(),
                               std::forward<Ts>(ts)...);
}
模板
constexpr decltype(自动)多个应用程序(F&&F,std::index_序列,Ts&&…Ts)
{
constexpr auto p=[](){
constexpr auto total_size=sizeof…(Is);
std::数组外部{};
std::数组内部{};
标准::大小\u t全局\u索引=0;
标准::尺寸外部值=0;
[[maybe_unused]]auto l=[&](std::size\u t size)
{
对于(std::size\u t i=0;i!=size;++i){
外部[全局_索引]=外部_值;
内部[全局指数]=i;
++全球统一指数;
}
++外部_值;
};
(l(std::tuple_size::value),…);
返回make_对(外部、内部);
}();
[[maybe_unused]]constexpr auto outer=p.first;
[[maybe_unused]]constexpr auto-inner=p.second;
使用std::get;
返回std::invoke(std::forward(f),
get(get(std::forward_作为元组(std::forward(ts)…)))…);
}
模板
constexpr decltype(自动)多个应用(F&&F,Ts&&Ts)
{
constexpr auto total_size=(std::size_t{0}+…+std::tuple_size::value);
返回多个应用impl(std::forward(f),
std::make_index_sequence(),
标准:转发(ts);
}

std::tuple\u cat有什么问题?您可以构建自己的tuple\u大小版本,并以递归方式进行工作,然后multi\u apply的实现与apply基本相同。您是否尝试优化编译持续时间?你的元组大小不一吗?如果这是关于我所认为的,
tuple\u cat
只要你在引用的元组上使用它就可以了。除非我们知道你认为tuple\u cat有什么问题,否则这个问题不是很有用,因为一个有效的答案可以用不同的名称重新实现tuple\u cat。另外,你把多个问题放在一个问题中,这是不受欢迎的。
std::tuple{std::forward(ts)…}
这只是
forward\u as\u tuple
,不是吗?@T.C.绝对正确。看看我那崭新的车轮。。。和另一个一样,但它是我的。很好的回答。正是我希望看到的!:)@VittorioRomeo干杯!我在结尾添加了一些实现说明。@VittorioRomeo这是由于libc++版本在
std::array
操作符[]
上缺少
constepr
。您可以使用arr_t=std::size_t[S]将
std::array
替换为
make_index
中的内置数组。事实上,我想我会在答案中加上这个。
template<class T, std::size_t S> constexpr T sum_array(const T (& a)[S], std::size_t i = 0)
{
   return i < S ? a[i] + sum_array(a, i + 1) : 0;
}
std::invoke(std::forward<F>(f), std::get<InnerIs>(std::get<OuterIs>(std::move(t)))...)
template <class F, std::size_t... Is, class ... Ts>
constexpr decltype(auto) multiple_apply_impl(F&& f, std::index_sequence<Is...>, Ts&&... ts)
{
    constexpr auto p = [](){
        constexpr auto total_size = sizeof...(Is);
        std::array<std::size_t, total_size> outer{};
        std::array<std::size_t, total_size> inner{};
        std::size_t global_index = 0;
        std::size_t outer_value = 0;

        [[maybe_unused]] auto l = [&](std::size_t size)
        {
            for (std::size_t i = 0; i != size; ++i) {
                outer[global_index] = outer_value;
                inner[global_index] = i;
                ++global_index;
            }
            ++outer_value;
        };
        (l(std::tuple_size<std::decay_t<Ts>>::value), ...);

        return make_pair(outer, inner);
    }();
    [[maybe_unused]] constexpr auto outer = p.first;
    [[maybe_unused]] constexpr auto inner = p.second;

    using std::get;
    return std::invoke(std::forward<F>(f),
                       get<inner[Is]>(get<outer[Is]>(std::forward_as_tuple(std::forward<Ts>(ts)...)))...);
}

template <class F, class ... Ts>
constexpr decltype(auto) multiple_apply(F&& f, Ts&&... ts)
{
    constexpr auto total_size = (std::size_t{0} + ... + std::tuple_size<std::decay_t<Ts>>::value);

    return multiple_apply_impl(std::forward<F>(f),
                               std::make_index_sequence<total_size>(),
                               std::forward<Ts>(ts)...);
}