The following code defines an operator+ between std::array of arbitrary type. The implementation consists in defining an auxiliary routine op2 that takes a generic callable operator and applies it to every element of the array, via template recursion.
#include <array>
#include <functional>
#include <iostream>
template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs);
template <std::size_t Index = 0, typename T, std::size_t N, class F>
void op2(const std::array<T, N>& lhs, const std::array<T, N>& rhs, std::array<T, N>& res, F&& op);
template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
{
std::array<T, N> res;
op2(lhs, rhs, res, std::plus{});
return res;
}
template <std::size_t Index, typename T, std::size_t N, class F>
void op2(const std::array<T, N>& lhs, const std::array<T, N>& rhs, std::array<T, N>& res, F&& op)
{
std::get<Index>(res) = op(std::get<Index>(lhs), std::get<Index>(rhs));
if constexpr (Index < (N - 1))
op2<Index + 1>(lhs, rhs, res, std::forward<F>(op));
}
template <std::size_t N>
void print_array(const std::array<int, N>& a)
{
for (int x : a)
std::cout << x << ' ';
std::cout << '\n';
}
int main(){
std::array<int, 3> a{1, 2, 3};
std::array<int, 3> b{2, 2, 1};
print_array(a + b);
std::array<std::array<int, 3>, 2> c{a, b};
std::array<std::array<int, 3>, 2> d{a, b};
auto x = c + d; // Error: no match for call to std::plus<void>
return 0;
}
See it Live on Coliru.
The first part works fine and computes the sum of the arrays of int a and b.
Next, c and d are std::array of std::array<int,3>. The program fails to compile because it cannot find a match for std::plus between components of c and d, that is, std::plus::operator() cannot understand how to sum two std::array<int, 3>.
A workaround is to define operator+ as
template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
{
std::array<T, N> res;
op2(lhs, rhs, res, [](const auto& lhs, const auto& rhs) { return lhs + rhs; });
return res;
}
See it Live on Coliru.
Accordind to the reference std::plus=std::plus<void> should simply return the sum result with parameter and return type deduced, pretty much similar to what the anonymous lambda does.
Why the solution with std::plus does not compile?
The same problem occurs in the following simplified demo:
std::array<int, 3> a{1, 2, 3};
std::array<int, 3> b{2, 2, 1};
print_array(std::plus{}(a, b));
Internally, std::plus invokes operator+. But since it is invoked in the std::namespace, it may not find your custom operator+ defined outside of this namespace.
Generally, operators for some class should be defined in the namespace where that class is defined. But in the case of std::array, this is not possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With