草庐IT

c++ - `constexpr` `std::array` 的二元运算

coder 2024-02-05 原文

我想写一个 constexpr 函数,减少给定的 std::array用二元运算。 IE。实现的函数

template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);

为了简单起见,我想从加法开始。例如

sum(std::array<int, 5>{{1,2,3,4,5}});  // returns 15.

到目前为止我得到了什么。

我使用常用​​的索引技巧来索引数组元素。 IE。生成 int序列,可用于通过参数列表扩展进行索引。

template <int... Is>
struct seq {};
template <int I, int... Is>
struct gen_seq : gen_seq<I - 1, I - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : seq<Is...> {};  // gen_seq<4> --> seq<0, 1, 2, 3>

sum然后通过可变模板递归定义函数。

// The edge-condition: array of one element.
template <typename T>
constexpr T sum(std::array<T, 1> arr, decltype(gen_seq<0>{})) {
    return std::get<0>(arr);
}

// The recursion.
template <typename T, std::size_t N, int... Is>
constexpr auto sum(std::array<T, N> arr, seq<Is...>) -> decltype(T() + T()) {
    return sum(std::array<T, N - 1>{ { std::get<Is>(arr)... } },
                gen_seq<N - 2>()) +
           std::get<N - 1>(arr);
}

// The interface - hides the indexing trick.
template <typename T, std::size_t N>
constexpr auto sum(std::array<T, N> arr)
    -> decltype(sum(arr, gen_seq<N - 1>{})) {
    return sum(arr, gen_seq<N - 1>{});
}

Here你可以看到它的实际效果。

问题

此实现有效。但是,现阶段我确实有几个问题。

  1. 有什么办法可以为这个函数添加 perfect-forward 吗?这有意义吗?或者我应该将这些数组声明为 const-references?
  2. 到目前为止的假设是,归约的返回类型是 decltype(T()+T()) . IE。添加两个元素时得到的结果。虽然在大多数情况下这对于加法应该是正确的,但对于一般的减少可能不再适用。有没有办法获得 a[0] + (a[1] + (a[2] + ... ) ) 的类型? ?我试过类似 this 的东西, 但我不知道如何生成 <T, T, T, ...> 的模板参数列表.

最佳答案

我的回答是基于我自己对此类人员的实现。

我更喜欢一般的 reduce(或 fold,或 accumulate)函数直接在元素上操作作为它自己的函数参数,而不是像 std::array 这样的容器。 .这样,不是在每次递归中都构造一个新数组,而是将元素作为参数传递,我想编译器更容易内联整个操作。另外它更灵活,例如可以直接使用或在 std::tuple 的元素上使用.一般密码是here .我在这里重复主要功能:

 template <typename F>
 struct val_fold
 {
    // base case: one argument
    template <typename A>
    INLINE constexpr copy <A>
    operator()(A&& a) const { return fwd<A>(a); }

    // general recursion
    template <typename A, typename... An>
    INLINE constexpr copy <common <A, An...> >
    operator()(A&& a, An&&... an) const
    {
       return F()(fwd<A>(a), operator()(fwd<An>(an)...));
    }
 };

很抱歉,这里充满了我自己的定义,所以这里有一些帮助:F是定义二元运算的函数对象。 copy是我对 std::decay 的概括在数组和元组中递归。 fwd只是std::forward的快捷方式.同样,common只是std::common_type的快捷方式但旨在进行类似的概括(一般来说,每个操作都可能产生一个表达式模板用于延迟评估,这里我们强制评估)。

你会如何定义sum使用以上?先定义函数对象,

struct sum_fun
{
    template <typename A, typename B>
    INLINE constexpr copy <common <A, B> >
    operator()(A&& a, B&& b) const { return fwd<A>(a) + fwd<B>(b); }
};

然后就

using val_sum = val_fold<sum_fun>;

当以 std::array 开头时,你会怎么调用它? ?好吧,一旦你有了你的 Is... , 你只需要

val_sum()(std::get<Is>(arr)...);

您可以将其包装在您自己的界面中。请注意,在 C++14 中,std::array::operator[]是 constexpr,所以这就是

val_sum()(arr[Is]...);

现在,回答您的问题:

1) 转发:是,std::get正在将数组元素转发到 val_sum ,它递归地将所有内容转发给自己。所以剩下的就是你自己的接口(interface)来转发输入数组,例如

template <typename A, /* enable_if to only allow arrays here */>
constexpr auto sum(A&& a) -> /* return type here */
{
    return sum(std::forward<A>(a), gen_seq_array<A>{});
}

等等,其中 gen_seq_array将采用 A 的原始类型( std::remove_refstd::remove_cv 等),推导出 N , 并调用 gen_seq<N>{} .如果数组元素具有移动语义,则转发是有意义的。它应该无处不在,例如val_sum的来电上面会是这样的

val_sum()(std::get<Is>(std::forward<A>(a))...);

2) 返回类型:如您所见,我正在使用 std::common_type 作为返回类型,应该为 sum 做和最常见的算术运算。这已经是可变的。如果您想要自己的类型函数,可以很容易地使用模板递归从二进制类型函数中创建可变参数。

我自己的版本commonhere .它有点复杂,但它仍然是一个包含一些 decltype 的递归模板。做实际的工作。

无论如何,这比您需要的更通用,因为它是为任意数量的任何给定类型定义的。你只有一种类型 T在你的阵列中,所以你拥有的应该足够了。

关于c++ - `constexpr` `std::array` 的二元运算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21840568/

有关c++ - `constexpr` `std::array` 的二元运算的更多相关文章

  1. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  4. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  5. arrays - 这是 Ruby 中 Array.fill 方法的错误吗? - 2

    这个问题在这里已经有了答案:Arraysmisbehaving(1个回答)关闭6年前。是否应该这样,即我误解了,还是错误?a=Array.new(3,Array.new(3))a[1].fill('g')=>[["g","g","g"],["g","g","g"],["g","g","g"]]它不应该导致:=>[[nil,nil,nil],["g","g","g"],[nil,nil,nil]]

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  8. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  9. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

  10. Ruby on Rails regexp equals-tilde 与 array include 用于检查选项列表 - 2

    我正在使用Rails3.2.3和Ruby1.9.3p0。我发现我经常需要确定某个字符串是否出现在选项列表中。看来我可以使用Ruby数组.includemethod:或正则表达式equals-tildematchshorthand用竖线分隔选项:就性能而言,一个比另一个好吗?还有更好的方法吗? 最佳答案 总结:Array#include?包含String元素,在接受和拒绝输入时均胜出,对于您的示例只有三个可接受的值。对于要检查的更大的集合,看起来Set#include?和String元素可能会获胜。如何测试我们应该根据经验对此进行测试

随机推荐