简体   繁体   中英

How to use user defined concept as a template type of std::span?

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});

Try it here .


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;
}

Try it here and here !

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM