I wanted to make an isIn function that takes an std::span.
This is my attempt:
#include <span>
template <typename T1, typename T2>
bool isIn(const T1& x, std::span<const T2> v)
{
for (const T2& e : v)
if (e == x)
return true;
return false;
}
// this one would work, but I want my function to be generic
/*bool isIn(int x, std::span<const int> v)
{
for (int e : v)
if (e == x)
return true;
return false;
}*/
int main()
{
const int v[] = {1, 2, 3, 4};
isIn(2, v); // I want this, but doesn't compile
//isIn(2, std::span<const int>(v)); // this works fine
}
As you can see, I can get around by doing this casting:
isIn(2, std::span<const int>(v));
But that's quite verbose, and I would like to do something like this:
isIn(2, v);
Is there any way that can be achived?
https://godbolt.org/z/czTs83
Here is a c++20 version of your code.
First we start off with two concepts; is_span and spannable:
template<class T>
concept is_span = requires(T& a) {
{ std::span(a) } -> std::same_as<T>;
};
template<class T>
concept spannable = requires(T& a) {
{ std::span(a) };
} && !is_span<T>;
Something spannable can be deduced into a span without being one.
Then we write two overloads:
constexpr bool isIn(const auto& x, is_span auto const& v)
{
for (const auto& e : v)
if (e == x)
return true;
return false;
}
constexpr bool isIn(const auto& x, spannable auto const& v)
{
return isIn(x, std::span(v));
}
using the new syntax.
We then add one more overload:
template<std::size_t N>
constexpr bool isIn(const auto& x, auto const(& v)[N])
{
return isIn(x, std::span(v));
}
which permits this tasty syntax:
static_assert( isIn( 7, {1,2,3,4,5,6,7} ));
Live example
Now all you have to do is make it infix.
That "sadly" means
static_assert( isIn('\0', "hello") );
is true, as "hello" is an array containing a '\0' at the end.
template<class T>
constexpr bool isIn(const auto& x, std::initializer_list<T> il)
{
return isIn(x, std::span(il));
}
template<std::size_t N>
constexpr bool isIn(char x, char const(& v)[N])
{
return isIn(x, std::span(v, v+N-1));
}
Live example.
There are no conversion/promotion for template deduction,
so const int (&)[4] cannot be deduced as std::span<const int /*, 4*/>.
You might still provide overload to do the conversion yourself (care to avoid infinite recursive call):
template <typename T1, typename T2, std::size_t N>
bool isIn(const T1& x, std::span<const T2, N> v)
{
// return std::find(std::begin(v), std::end(v), x) != std::end(v);
for (const T2& e : v) {
if (e == x) {
return true;
}
}
return false;
}
template <typename T, typename C>
bool isIn(const T& x, const C&c)
{
return isIn(x, std::span(c)); // Use CTAD for the conversion.
}
But std::span is not needed here:
template <typename T, typename C>
bool isIn(const T& x, const C& c)
{
// return std::find(std::begin(c), std::end(c), x) != std::end(c);
for (const auto& e : c) {
if (e == x) {
return true;
}
}
return false;
}
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