Преобразование указателя в диапазон - PullRequest
4 голосов
/ 14 июля 2020

Это работает:

const char* foo[] = {"This", "is", nullptr, "great"};
for (auto e : ::std::ranges::take_while_view (foo,             // <- array
                    ([](const char* s){return s!=nullptr;})))
      std::cout << e << "\n";

Это не работает:

const char* foo[] = {"This", "is", nullptr, "great"};
for (auto e : ::std::ranges::take_while_view (&foo[0],         // <- pointer
                    ([](const char* s){return s!=nullptr;})))
      std::cout << e << "\n";

error: no matching function for call to ‘take_while_view(const char**,
       main(int, char**)::<lambda(const char*)>)’

Можем ли мы, используя стандартные типы библиотеки и функции , принудить C ++ обрабатывать указатель как своего рода полубесконечный диапазон, который мы можем затем дополнительно ограничить?

Я всегда могу создать свой собственный класс для представления полубесконечного представления, но я бы предпочел найти решение в стандарте библиотека.

Ответы [ 2 ]

4 голосов
/ 14 июля 2020

Существует способ собрать такой диапазон из разрозненных частей, но вы не можете неявно рассматривать T* как единое целое.

Все, что вам нужно сделать, это построить subrange из указателя, используя unreachable_sentinel_t в качестве дозорного типа. Таким образом, вы можете закодировать такую ​​функцию как:

template<typename T>
auto inf_ptr_range(T *ptr)
{
  return std::ranges::subrange(ptr, std::unreachable_sentinel_t{});
}

И вы можете использовать это в своем коде :

const char* foo[] = {"This", "is", nullptr, "great"};
for (auto e : ::std::ranges::take_while_view (
        inf_ptr_range(foo),
        ([](const char* s){return s!=nullptr;})))
    std::cout << e << "\n";

Или используя нотацию в стиле представления:

int main()
{
const char* foo[] = {"This", "is", nullptr, "great"};
for (auto e : inf_ptr_range(foo) | std::views::take_while(
        [](const char* s){return s!=nullptr;}))
    std::cout << e << "\n";
}
1 голос
/ 14 июля 2020

Используйте iota, чтобы создать бесконечный диапазон, а затем обрежьте его.

auto my_range =
    std::views::iota(&foo[0])
    | std::views::transform([](auto p) -> auto& { return *p; })
    | std::views::take_while([](auto p) { return p != nullptr; });

for(auto &ptr : my_range) {
    std::cout << ptr << "\n";
    ptr = "really";
}
for(auto &ptr : my_range) std::cout << ptr << "\n"; // mutation works!
std::cout << foo[3] << "\n";

Godbolt

Я считаю, что это безопасно (что take_while «охраняет» transform и iota, чтобы мы никогда не пытались вычислить &foo[0] + 5 или даже получить доступ к foo[3]).

...