Spans are views into contiguous buffers of that specific object, not abstractions of covariance.
I'd just pass a span of strings. KISS.
Assuming I end up havig a reason to be super fancy and need a single stable API, a span of foo is poor. You want multiple spans of foo that are provided on demand. If the spans are large enough, the overhead becomes cheap, bht this permits caller to not allocate all data at once.
template<class T>
using ask_for=function_view<void(T)>;
template<class T>
using provide=ask_for<ask_for<T>>;
then we take:
void do_stuff(provide<std::span<std::string_view>> src){
src([](std::span<std::string_view>> strings){
// code consuming strings
});
}
on the provider side, we can make a fixed buffer of string views, wrapping pieces of the std vector of string, and repeatedly pass the fixed buffer (with new strings) to the callback.
void provide_strings(std::span<std::string> strings){
std::size_t count = 0;
do_stuff([&](ask_for<std::span<std::string_view>> sink){
std::array<std::string_view, 1024> views;
while(count<strings.size()){
for(std::size_t i = count; i <std::min(count+1024, strings.size());++i){
views[i-count]=strings; // I think this isn't legal, but you get the idea
}
sink( {views.data(), std::min(strings.size()-count, 1024ull)});
count += 1024;
}
});
}
ok a bit pseudocodey. But I hope you get the idea.