c++ - 改进折叠功能

标签 c++ c++17 variadic-templates template-meta-programming fold-expression

我在 C++ 中实现了一个简单的折叠函数,它接受一个 lambda,并且可以在编译时同时折叠多个 vector 。我想知道是否可以通过某种方式简化它(我提供了递归版本和迭代递归版本 - 我不确定哪个应该具有更好的性能):https://godbolt.org/z/39pW81

也欢迎性能优化 - 在这方面,这两种方法中的任何一种都更快吗?

template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail,  typename Function>
auto foldHelperR(Function&& func, const type_identity& id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
    if constexpr (I>0)
    {
        return func(foldHelperR<I-1>(std::forward<Function>(func), id, head, tail...), head[I], tail[I]...);
    }
    else
    {
        return func(id, head[0], tail[0]...);
    }
}

template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail,  typename Function>
auto foldHelperI(Function&& func, const type_identity id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
    if constexpr (I<N-1)
    {
        return foldHelperI<I+1>(std::forward<Function>(func), func(id, head[I], tail[I]...), head, tail...);
    }
    else
    {
        return func(id, head[N-1], tail[N-1]...);
    }
}

template<typename type_identity, typename type_head, int N_head, typename ...type_tail, int ...N_tail, typename Function = void (const type_identity&, const type_head&, const type_tail&...)>
constexpr auto fold(Function&& func, const type_identity& id, const tvecn<type_head, N_head>& head, const tvecn<type_tail, N_tail>&... tail)
{
    static_assert(std::is_invocable_v<Function, const type_identity&, const type_head&, const type_tail &...>,
     "The function cannot be invoked with these zip arguments (possibly wrong argument count).");
    static_assert(all_equal_v<N_head, N_tail...>, "Vector sizes must match.");

    //return foldHelperR<N_head-1>(std::forward<Function>(func), id, head, tail...);
    return foldHelperI<0>(std::forward<Function>(func), id, head, tail...);
}

int main()
{
    tvecn<int,3> a(1,2,3);
    return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
}

最佳答案

and can fold multiple vectors at the same time at compile time

不完全是:如果你想运行编译时

(1) 你必须定义 constexpr tvecn 构造器和

(2) 你必须定义constexpr foldhelper 函数和

(3) 你必须声明constexpr a

 // VVVVVVVVV
    constexpr tvecn<int,3> a(1,2,3);

(4) 您必须将 fold 的结果放在 constexpr 变量中(或者,更一般地说,放在需要编译时间的地方,作为 C 的 size 字段-样式数组,或模板值参数,或 static_assert() 测试)

constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
                        0, a, a);

I am wondering if it could be simplified in some manner

当然可以。

首先:如果可以,请避免重新发明轮子:您的 tvecnstd::array 的简化版本。

建议:使用std::array(如果可以的话)

第二:你标记了 C++17,这样你就可以使用折叠

建议:all_equal也使用它

template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
 { };

template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;

一般情况下:当您必须定义必须提供数字的自定义类型特征时,继承(如果可能)自 std::integral_constant(或 std::bool_constant,或 std::true_type,或 std::false_type:所有 std::integral_constant 特化)。因此,您自动继承了所有 std::integral_constant 设施。

第三:几乎所有 C++ 标准都使用 std::size_t,而不是 int 来表示大小。

建议:当你必须处理尺寸时,使用std::size_t,而不是int。这样你就可以避免很多烦人的麻烦。

第四:从 main() 你应该只返回 EXIT_SUCCESS(通常为零)或 EXIT_FAILURE(通常为 1)

建议:避免事情

return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);

第五:永远不要低估逗号运算符的力量。

建议:完全避免递归,对辅助函数也使用模板折叠;举例说明

template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
                            F const & f, T id, As const & ... arrs)
 { return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }

您可以从 fold()

中按如下方式调用
return foldHelperF(std::make_index_sequence<N_head>{}, 
                   std::forward<Function>(func),
                   id, head, tail...);

下面是一个完整的编译和简化的例子

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

template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
 { };

template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;


template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
                            F const & f, T id, As const & ... arrs)
 { return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }


template <typename type_identity, typename type_head, std::size_t N_head,
          typename ...type_tail, std::size_t ...N_tail,
          typename Function = void (type_identity const &,
                                    type_head const &,
                                    type_tail const & ...)>
constexpr auto fold (Function && func, type_identity const & id,
                     std::array<type_head, N_head> const & head,
                     std::array<type_tail, N_tail> const & ... tail)
 {
   static_assert( std::is_invocable_v<Function, const type_identity&,
                  const type_head&, const type_tail &...>,
                  "The function cannot be invoked with these zip arguments"
                  " (possibly wrong argument count).");

   static_assert( all_equal_v<N_head, N_tail...>,
                 "Vector sizes must match.");

   return foldHelperF(std::make_index_sequence<N_head>{}, 
                      std::forward<Function>(func),
                      id, head, tail...);
}

int main()
 {
   constexpr std::array<int, 3u> b{2, 5, 7};

   constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
                           0, b, b);

   std::cout << f << std::endl;
 }

关于c++ - 改进折叠功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58483377/

相关文章:

c++ - VC++中Float数据类型的值,如何判断是否混合了字符串或特殊字符

c# - 托管函数在非托管结构(C++、C#)中作为回调函数传递的问题

c++ - 如何正确使用 OpenH264 使用代码示例进行编码?

c++ - 关于隐式声明的复制构造函数的引用在逻辑上不清楚

c++ - std::string_view 的自定义序列化导致意外的编译器错误

c++ - 使用简单类型列表实现的指数编译时间。为什么?

c++ - 迭代多个模板参数的递归模板函数

c++ - 轻松检查共享库中未解析的符号?

c++ - C++11 可变参数模板中的 va_arg() 是什么?

c++ - GTest 参数化测试从数组或类似文件中解压值参数