Можно ли построить `std::span` из представления в C++20?

Этот пример программы не компилируется, потому что transform_view нельзя преобразовать в std::span:

class Foo {
private:
    std::vector<std::string> strings = { "a", "b", "c" };

public:
    std::span<const char*> getStrings() {
        return strings | std::views::transform([](const std::string& str) { return str.c_str(); });
    }
};

int main() {
    Foo foo;
    auto strings = foo.getStrings();

    for (auto s : strings)
        std::cout << s << std::endl;
}

Я знаю, что пока нельзя построить контейнеры (типа std::vector), однако я не совсем понимаю, почему из него нельзя построить std::span. Я нашел этот ответ, в котором говорилось, что в настоящее время единственным контейнером, который может быть создан из произвольного диапазона, является std::span, поэтому я ожидал, что приведенный выше пример будет работать.

Есть ли способ создать диапазон из диапазона? Или есть другой способ вернуть общее представление из метода без использования auto (что не разрешено для виртуальных методов)?


person Carsten    schedule 12.05.2021    source источник
comment
Нет std::span не может быть построено из произвольного диапазона. Это просто легковесный просмотрщик непрерывного диапазона.   -  person ALX23z    schedule 12.05.2021


Ответы (2)


Можно ли построить std::span из представления в С++ 20?

Диапазон можно построить из любого непрерывного диапазона (соответствующего базового типа). Проблема здесь:

std::span<const char*> getStrings() {
    return strings | std::views::transform([](const std::string& str) { return str.c_str(); });
}

заключается в том, что созданный вами адаптированный диапазон не является непрерывным, это всего лишь произвольный доступ. span<char const*> должно ссылаться на char const*, которые находятся в памяти непрерывно, и здесь определенно не будет такого случая. Вот почему это не работает.

Но это не означает, что нет адаптированных диапазонов, которые можно преобразовать в span. Например, это будет работать:

std::span<std::string> getStrings() {
    return strings | std::views::take(5);
}

Поскольку views::take может сохранять непрерывность (в отличие от transform по причинам, которые, надеюсь, ясны).

person Barry    schedule 12.05.2021
comment
Спасибо, это имеет смысл. А как насчет второй части моего вопроса? Я хочу иметь возможность передать представление обратно вызывающей стороне, однако мой метод на самом деле является частью интерфейса, поэтому он virtual, и я не могу сделать что-то вроде constexpr auto getStrings(). Должен ли я создавать и возвращать массив или вектор в этом случае? - person Carsten; 12.05.2021
comment
@Carsten В вашем случае вы можете вернуть span<string> без преобразования (что в любом случае лучше, чем span<char const*>). В противном случае, vector, вероятно, будет хорошей ставкой. - person Barry; 13.05.2021
comment
спасибо, я думаю, что span<string> подойдет для большинства случаев. Однако мне нужно получить массив const char* для некоторого C API. Тем не менее, я думаю, что в этом случае я буду использовать какой-нибудь помощник по преобразованию, чтобы облегчить vector, который затем хранит указатели. Благодарю вас! ???? - person Carsten; 13.05.2021

std::span - это указатель и размер. Он указывает на массив (или никуда). Нет массива const char *.

Вы также не можете получить std::span из std::deque.

person Caleth    schedule 12.05.2021