1

This question has been answered before by using ranges, but I'm using C++17 with a backport of std::span - so I have spans but not ranges.

Consider this simple function:

std::span<std::string_view> cmdline_args_to_span(int argc, const char* argv[])
{
    return std::span<const char*>(argv, argc);
}

I thought the converting constructor of span would kick in due to the const char* constructor of std::string_view but it isn't. And neither is:

std::span<std::string_view> cmdline_args_to_span(int argc, const char* argv[])
{
    auto c_args = std::span<const char*>(argv, argc);
    return std::span<std::string_view>(c_args);
}

Or:

std::span<std::string_view> cmdline_args_to_span(int argc, const char* argv[])
{
    auto c_args = std::span<const char*>(argv, argc);
    return std::span<std::string_view>(c_args.begin(), c_args.end());
}

All of these fail on both Clang and gcc (using the 'real' std::span but it's the same for my backported version).

I must be doing something stupid, but I can't see what - can anyone help?

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
cmannett85
  • 21,725
  • 8
  • 76
  • 119

1 Answers1

3

Convert main args to `spanstd::string_view without ranges

This question has been answered before

You'll notice that the "answered before" solution doesn't create a std::span<std::string_view> at all.

std::span is not an adapter range whose iterators would generate objects upon indirection such as std::views::transform is and does. You cannot have a std::span<std::string_view> unless you have an array of std::string_view objects. And those objects won't be stored within the span.

std::span<std::string_view> isn't very convenient for command line arguments. But, you could first create a std::vector<std::string_view> and then a span pointing to the vector. Since command line arguments are global, this is a rare case where a static storage doesn't suffer from significant problems - unless the caller mistakenly thinks that they can call it with varying arguments:

std::span<std::string_view>
cmdline_args_to_span(int argc, const char* argv[])
{
    static std::vector<std::string_view> args(argv, argv + argc);
    return args;
}

but I'm using C++17 with a backport of std::span - so I have spans but not ranges.

Ranges can be had pre-C++20 even though the standard library doesn't have them. In fact, the standard ranges are based on a library that works in C++14.

That said, in case you aren't interested in using any of the pre-existing range libraries, then implementing a transform view yourself involves quite a bit of boilerplate.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Why don't you just return the `vector` instead? – Passer By Mar 08 '22 at 19:54
  • @PasserBy Then you wouldn't have a `std::span` that was asked for :) – eerorika Mar 08 '22 at 19:56
  • I'm kicking myself, you're absolutely right. _Obviously_ a span can't transform the element types as it doesn't own the underlying storage, I'd need a transforming view which I need the ranges library for. The vector idea I can't use as the primary motivation for using a span was to avoid an allocation (good idea though!), so I guess I'll have to fall back to `span`. – cmannett85 Mar 08 '22 at 20:18