I would like to have only one template function. So I came up with like...
template<typename Iteratable, size_t N,
typename =
std::enable_if_t<
std::is_same_v<Iteratable, const std::initializer_list<size_t> > ||
std::is_same_v<Iteratable, const std::array<size_t, N > >
>
>
std::ostream& operator << (std::ostream& os, Iteratable& in) {
std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ") );
return os;
}
It seems because of N
in std::array<size_t, N>
, the specialization fails.
Is there any how not to write 2 function for this use case?
If the sole reason you don't want to overload is to avoid duplicating the function body, you can instead go for writing your own trait. One such way:
namespace details {
template<class Iterable>
struct writable : std::false_type {};
template<size_t N>
struct writable<std::array<std::size_t, N>> : std::true_type {};
template<>
struct writable<std::initializer_list<size_t>> : std::true_type {};
template<class Iterable>
constexpr bool writable_v = writable<Iterable>::value;
}
template<typename Iteratable,
std::enable_if_t<details::writable_v<std::decay_t<Iteratable>>,
int> = 0
>
std::ostream& operator << (std::ostream& os, Iteratable& in) {
std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ") );
return os;
}
I also took the liberty of moving the enable_if
to the template argument. That way SFINAE cannot be circumvented by specifying a type name for both arguments (though admittedly, it's unlikely to happen for an overloaded operator).
Another nice fact about it is that the customization point is now decoupled from the function definition itself. Adding new iterables is just a matter of adding another specialization for details::writable
.
Live Example
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