C++ constexpr 函数校验数组

C++ constexpr function to checksum an array

是否有办法实现下面示例(非编译)代码中所示的行为?我(想我)明白为什么它不能编译,因为对 std::initializer_list 函数的必需调用不是 constexpr。

目标是在将附加元素附加到作为前面元素总和的末尾时,能够从常量初始化列表创建数组。

我找到的所有关于在编译时初始化数组的帖子都需要大量复杂的递归模板函数调用,并且都与生成数字序列有关。

#include <initializer_list>
#include <array>

template <typename T> constexpr auto CheckSummedArray(const std::initializer_list<T> & i)
{
    std::array<T, i.size() + 1> res;
    std::copy(i.begin(), i.end(), res.begin());
    auto cs = T();
    for (auto r : i)
    {
        cs += r;
    }
    res[res.size() - 1] = cs;
    return res;
}

constexpr auto testArray = CheckSummedArray<int>({1,2,3,4});

static_assert(testArray.size() == 5);
static_assert(testArray[0] == 1);
static_assert(testArray[4] == 9);

问题不在于调用 std::initializer_list 的成员,这些函数实际上是 constexpr。问题是您在模板参数中使用 i.size() 的结果,但 i 不是常量表达式,因为它是函数参数。

你可以通过将参数设为数组类型来解决这个问题,这样你就可以用模板参数推导出它的大小:

template <typename T, std::size_t N> 
constexpr auto CheckSummedArray(T const(&i)[N])
{
    std::array<T, N + 1> res{};
    std::copy(i, i + N, res.begin());
    res.back() = std::accumulate(i, i + N, 0);
    return res;
}

这里是 demo

我通过使用算法而不是循环稍微清理了函数。如果您不使用 C++20,则无法访问 constexpr 算法,因此您的 copyaccumulate 无论如何都需要是原始循环。

for(std::size_t j = 0; j < N; ++j)
{    
    res[j] = i[j];
    res.back() += i[j];
}

这里是 C++17 demo