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

标签 c++ arrays c++11

我想写一个 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++ - 从我的程序通过 gui 控制其他应用程序

c++ - 生成可重现的大数序列 - 使用伪随机生成器?

c++ - 通过套接字发送二进制文件

java - 从 jsonarray 创建的 jsonobject 总是空的错误不为空

c++ - gcc 4.8.1 是否支持 C++11 垃圾回收?

c++ - 未找到符号又名 undefined symbol

c - C 中二进制搜索的第一次和最后一次出现

java - 如何格式化数组元素?

multithreading - 在 Linux 2.6+ 中替换系统调用(syscalls)

c++ - 有没有办法在 C++11 中取消/分离 future ?