I would like to use my user defined concept as a template type of std::span but template argument deduction does not work as I expected. When I try to pass a " std::array
of char
" to template function; compiler shows an error " error: no matching function for call to 'print' " and warns me when I hover over the template definition with mouse as "note: candidate template ignored: could not match 'span' against 'array'" .
Here is the concept definition and function template:
#include <concepts>
#include <span>
template <typename T>
concept OneByteData = sizeof(T) == 1;
template<OneByteData T>
void print(std::span<const T> container)
{
for(auto element : container)
{
//Do Some Work
}
}
And the user code that doesn't work as I expected:
int main()
{
std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
print(arr);
return 0;
}
The user code that works and does not produce error:
int main()
{
std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
print<char>(arr);
return 0;
}
Is there way to call this template function without specializing the type of array. How should I change the template function definition to make the function call the way I mentioned ( print(arr)
)?
Edit: I would like to be able to use the benefits of std::span and be able to call the template function with std::array, std::vector and plain C-style arrays.
A possible solution is receive (and deduce) a generic type and after check if it is convertible to a std::span
with an element_type
of size 1.
I mean
template <typename T>
concept OneByteData = sizeof(T) == 1;
template <typename T>
void print (T container)
requires OneByteData<typename decltype(std::span{container})::element_type>
{
std::span cnt {container};
for(auto element : cnt )
{
//Do Some Work
}
}
// extra print for C-style cases
template <typename T, std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }
Another possible solution is a print()
, similar to your original, that receive a std::span
and do the concept check, and a couple of additional print()
(one specific for C-style arrays) that convert the deduced type to std::span
template <typename T>
concept OneByteData = sizeof(T) == 1;
template<OneByteData T, std::size_t N>
void print(std::span<T, N> container)
{
for(auto element : container)
{
//Do Some Work
}
}
template <typename T, std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }
template <typename T>
void print (T container)
{ print(std::span{container}); }
From my understanding this is how you should actually use it:
// Second template parameter for size
template<OneByteData T, std::size_t N>
void print(std::span<T, N> container) {
// Do something
return;
}
// Create a span from a plain array
print(std::span{arr});
If you do not want your user to do them by themselves, you could
Write an overload for print
that handles the conversion from std::array
to std::span
such as ( Wandbox )
template<OneByteData T, std::size_t N> void print(std::array<T,N> const& container) { return print<T const>(std::span{container}); }
Otherwise you could rewrite the interface of print
to take any container , enforce your constraints for the underlying element type with std::enable_if
or concepts such as user max66 has proposed and convert this general container to an std::span
internally.
For a class you could write a template deduction guide which decides which constructor should be used ( Wandbox )
template <typename T, std::size_t N> Print(std::array<T,N>) -> Print<T,N>;
Edit
In the case of an operator like in your case as discussed in the comments I would actually use the combination of a templated function and a templated operator . The function append
does the work using a generic std::span
for the OneByteData
data type while the templated operator converts the allowed data types to std::span
and calls the function. OneByteData<typename decltype(std::span{cont})::element_type>
makes sure that the data structure can be converted to a std::span
with the correct data type. You might add additional or different constraints to it or combine these constraints to your own concept.
template<OneByteData T, std::size_t N>
void append(std::span<T,N> const& sp) {
// Do something
}
template <typename Cont>
MyString& operator += (Cont const& cont)
requires OneByteData<typename decltype(std::span{cont})::element_type> {
this->append(std::span{cont});
return *this;
}
I would like to use my user defined concept as a template type of std::span but template argument deduction does not work as I expected. When I try to pass a " std::array
of char
" to template function; compiler shows an error " error: no matching function for call to 'print' " and warns me when I hover over the template definition with mouse as "note: candidate template ignored: could not match 'span' against 'array'" .
Here is the concept definition and function template:
#include <concepts>
#include <span>
template <typename T>
concept OneByteData = sizeof(T) == 1;
template<OneByteData T>
void print(std::span<const T> container)
{
for(auto element : container)
{
//Do Some Work
}
}
And the user code that doesn't work as I expected:
int main()
{
std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
print(arr);
return 0;
}
The user code that works and does not produce error:
int main()
{
std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
print<char>(arr);
return 0;
}
Is there way to call this template function without specializing the type of array. How should I change the template function definition to make the function call the way I mentioned ( print(arr)
)?
Edit: I would like to be able to use the benefits of std::span and be able to call the template function with std::array, std::vector and plain C-style arrays.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.