I have the following function:
#include <algorithm>
#include <iostream>
#include <numeric>
#include <ranges>
#include <span>
#include <type_traits>
template <typename T>
concept arithmetic = std::is_arithmetic_v<T>;
template <arithmetic T, std::size_t N>
auto sum(std::span<T, N> const a,
std::span<T, N> const b,
std::span<T, N> const c)
{
std::ranges::transform(a, b, std::begin(c),
[](auto const a, auto const b) { return a + b; });
}
When I try to call it like the following:
std::array<int, 10> a{};
std::array<int, 10> b{};
std::array<int, 10> c{};
// more initialisation for a, b, and c
// ...
sum(a, b, c);
I receive the following template deduction error:
<source>:41:5: error: no matching function for call to 'sum'
sum(a, b, c);
^~~
<source>:15:10: note: candidate template ignored: could not match 'span' against 'array'
auto sum(span_const_of_const<T, N> a,
^
I need to fix the call with explicit constructors to std::span
, like so:
sum(std::span(a), std::span(b), std::span(c));
I was under the impression that std::span
meant I didn't have to do this. What am I doing wrong here?
Template argument deduction does not consider conversions. If you don't deduce the arguments then you don't have to specify span.
sum<int, 10>(a, b, c);
Alternatively, you could deduce the arguments and constrain them to allow construction of a std::span<arithmetic, N>
template <typename T>
concept arithmetic_span_like = std::ranges::range<T>
&& arithmetic<std::ranges::range_value_t<T>>
&& std::constructible_from<std::span<std::ranges::range_value_t<T>>, T>;
template <typename A, typename B>
concept same_size = arithmetic_span_like<A>
&& arithmetic_span_like<B>
&& requires(A a, B b)
{
std::ranges::size(a) == std::ranges::size(b);
};
template <arithmetic_span_like A, arithmetic_span_like B, arithmetic_span_like C>
requires same_size<A, B> && same_size<A, C>
auto sum(A&& a, B&& b, C&& c)
{
std::ranges::transform(a, b, std::begin(c), std::plus{});
}