【问题标题】:Initialize a constexpr array as sum of other two constexpr arrays将 constexpr 数组初始化为其他两个 constexpr 数组的总和
【发布时间】:2013-11-12 18:15:38
【问题描述】:

给定两个 constexpr 数组(type[N]std::array<type, N>

constexpr int A[5] { 0, 1, 2, 3, 4 };
constexpr int B[5] { 5, 4, 3, 2, 1 };

是否可以初始化一个新的constexpr 数组执行逐元素操作(或constexpr 函数)?

比如这段代码可以

constexpr int sum(int i) { return A[i] + B[i]; }
constexpr int S[5] { sum(0), sum(1), sum(2), sum(3), sum(4) };

以更方便的形式重写,为S 中的每个元素调用sum(i)

【问题讨论】:

标签: c++ arrays c++11 initialization initializer-list


【解决方案1】:

类似的事情已经做过很多次了,但是这里有一个解决方案这个特定的数组编译时操作;)

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...> {};

#include <array>

template<class T, int N, int... Is>
constexpr std::array<T, N> sum(T const (&lhs)[N], T const (&rhs)[N], seq<Is...>)
{
    return {{lhs[Is]+rhs[Is]...}};
}

template<class T, int N>
constexpr auto sum(T const (&lhs)[N], T const (&rhs)[N])
-> decltype( sum(lhs, rhs, gen_seq<N>{}) )
{
    return sum(lhs, rhs, gen_seq<N>{});
}

#include <iostream>
int main()
{
    constexpr int a[] = {1,2,3,4,5};
    constexpr int b[] = {1,2,3,4,5};
    constexpr auto c = sum(a,b);

    for(auto e : c) std::cout << e << ", ";
}

注意std::array::operator[] 在 C++11 中 不是 constexpr,因此我使用原始数组作为输入。


对于任意二元函数:

template<class T, int N, class F, int... Is>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f,
                         seq<Is...>)
-> std::array<decltype( f(lhs[0], rhs[0]) ), N>
{
    return {{ f(lhs[Is], rhs[Is])... }};
}

template<class T, int N, class F>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f)
-> decltype( transform(lhs, rhs, f, gen_seq<N>{}) )
{
    return transform(lhs, rhs, f, gen_seq<N>{});
}

constexpr int sum(int l, int r) { return l+r; }
// ...
constexpr auto c = transform(a,b,sum);

对于任意 n 元函数和任意类数组类型:

template<class F, class... Args>
constexpr auto index_invoke(F f, int i, Args&&... args)
-> decltype( f(args[i]...) )
{
    return f(args[i]...);
}

template<class F, int... Is, class... Args>
constexpr auto transform_impl(F f, seq<Is...>, Args&&... args)
-> std::array<decltype( f(args[0]...) ), sizeof...(Is)>
{
    return {{ index_invoke(f, Is, std::forward<Args>(args)...)... }};
}

template <class T, class...>
struct get_extent_helper
: std::integral_constant<int,
                         std::extent<typename std::remove_reference<T>::type>{}>
{};

template<class F, class... Args>
constexpr auto transform(F f, Args&&... args)
-> decltype( transform_impl(f, gen_seq< get_extent_helper<Args...>{} >{},
                            std::forward<Args>(args)...) )
{
    using N = get_extent_helper<Args...>;
    return transform_impl(f, gen_seq<N{}>{}, std::forward<Args>(args)...);
}

使用别名模板更轻量:

template <class T, class...>
using get_extent_helper =
  std::integral_constant<int,
                         std::extent<typename std::remove_reference<T>::type>{}>;

但这在 g++4.8.1 下是有问题的

【讨论】:

  • 太棒了!我从这个答案中学到了很多东西,非常感谢!
  • nitpick: std::get is constexpr for std::arrays
  • @TomKnapen “自 C++14 起,重载被标记为 constexpr。” :(
  • 对于任何担心上述性能的人,我进行了广泛的测试,使用 lambda's for F 与简单循环来编译他的转换“任意二进制”,并且在所有情况下,使用优化设置,创建的代码在 x86 上是相似的,如果不完全相同的话。在大型数组中,编译器创建了一个循环函数和一个短函数,它只是展开了两者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 2017-07-02
  • 2017-06-01
相关资源
最近更新 更多