C++ constexpr function to checksum an array

Is there away to achieve the behavior shown in the example (non compiling) code below? I (think I) understand why it doesn't compile given that the required calls to std::initializer_list functions are not constexpr.

The goal is to be able to create an array from a constant initialiser list when an additional element is appended to the end that is the sum of the preceeding elements.

All the posts I found on initialising arrays at compile time required lots of complex recursive template function calls and all were related to generating sequences of numbers.

#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);

1 answer

  • answered 2021-04-08 03:38 cigien

    The issue is not the calls to the members of std::initializer_list, those functions are actually constexpr. The issue is that you are using the result of i.size() in a template parameter, but i is not a constant expression, since it's a function parameter.

    You can solve this by making the argument an array type, and so you can deduce its size with a template parameter:

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

    Here's a demo.

    I cleaned up the function a little by using an algorithm instead of a loop. If you're not using C++20, you don't have access to constexpr algorithms, so your copy and accumulate will need to be raw loops anyway.

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

    Here's a C++17 demo.