Использование std:: accumulate на двумерном std:: array

Для двумерного массива

std::array<std::array<int, 2>, 3> m = {{ {1, 2}, {3, 4}, {5, 6} }};

Я ищу сумму всех его элементов - в этом случае 21. Если бы массив был одномерным, я мог бы написать

auto sum = std::accumulate(m.begin(), m.end(), 0);

но для моего двумерного массива это не удается с довольно понятной ошибкой

no match for 'operator+' (operand types are 'int' and 'std::array<int, 2ul>')

Как я могу изящно вычислить эту сумму для моего 2D-массива (избегая for-loops, предпочитая STL-алгоритмы)?

Можно ли сделать однострочный, как для одномерного случая, или он становится более сложным?

Ответ 1

Это немного сложнее. Вы должны вложить 2 std::accumulate вызовов. Вложенный вызов std::accumulate суммирует элементы во вложенных массивах, а затем первый std::accumulate суммирует их.

auto sum = std::accumulate(m.cbegin(), m.cend(), 0, [](auto lhs, const auto& rhs) {
    return std::accumulate(rhs.cbegin(), rhs.cend(), lhs);
});

Это решение С++ 14 из-за общей лямбда, но для С++ 11 вам просто нужно явно указать типы.

Ответ 2

Концептуально вы хотите сгладить массив m, а затем примените его к нему. Используя библиотеку Range-v3 (или Ranges TS в будущем), вы можете сделать именно это (ссылка на wandbox).

std::array<std::array<int, 2>, 3> m = {{ {1, 2}, {3, 4}, {5, 6} }};

auto result = ranges::accumulate(ranges::join(m), 0); // flatten range then apply accumulate

Это работает так, как упоминал Пит Беккер в комментарии: "Проходите через одну строку массива и когда он попадает в конец строки, переходите к следующей строке". Никакой копии сделанных поддиапазонов.