cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Конструктор стандартных контейнеров от итераторов разного типа и Ranges #458

Closed kirillgrachoff closed 2 years ago

kirillgrachoff commented 3 years ago

Почему больно без этого

Возьмём код на С++. Вполне обычный код.

std::vector<char> v;
// something useful
std::string s(v.begin(), v.end());

Работает.

Теперь вспомним про замечательные Ranges.\ Что теперь хочется?\ А хочется вот так:

std::string text = "this is simple text";
for (auto subs : std::ranges::split_view(text, ' ')) {
    // либо так:
    std::string word(std::ranges::begin(subs), std::ranges::end(subs));
    // либо так:
    std::string word(subs);
}

Но ни то, ни другое не компилируется т.к. у std::string (и у других контейнеров) нет таких штук.

Предложение

Добавить к каждому std контейнеру хотя бы конструктор от двух итераторов разного типа (это несложно). Всего лишь добавить

template <typename InputIterFirst, typename InputIterLast>
requires Comparable<InputIterFirst, InputIterLast>
std::string(InputIterFirst first, InputIterLast last) {}

Где Comparable<T, U> - это concept, отвечающий за то, чтобы их можно было сравнивать друг с другом.

AKonia commented 3 years ago

А разве вот этот конструктор не тот, что вы ищете ?

template< class InputIt >
basic_string( InputIt first, InputIt last, 
              const Allocator& alloc = Allocator() );

Да и разве это нормально, чтобы итераторы на основе которых вы создаёте строку имели разный тип, это чисто логически как ? Типо от элемента одного контейнера до элемента другого ? Подумайте, может чего-то просто не поняли и есть способ сделать то, что вы предлагаете по-другому

kirillgrachoff commented 3 years ago

Нет, это не тот конструктор.

std::ranges::split_view и другие Ranges могут иметь своим началом и концом итераторы разного типа. Самое главное, чтобы они были сравниваемы. Попробуйте следующий код:

std::string s = "hello world";
auto v = std::ranges::split_view(s, ' ');
for (auto i : v) {
    std::cout << std::is_same_v<decltype(std::ranges::begin(i)), decltype(std::ranges::end(i))> << '\n';
}

Как это логически: хочется за O(1) копировать view. Но хочется по нему итерироваться. Хочется за O(1) находить конец. Решение - сделать так, чтобы end возвращал другой тип, который бы дёргал объект и спрашивал, есть ли там следующий.

tomilov commented 3 years ago

Кажется предложения для sentinel-ов уже давно есть и что-то уже есть в C++20 experimental. Наверняка и для контейнеров уже всё продумывается.

tomilov commented 3 years ago

Про конструирование контейнеров, которое затруднено на самом деле: https://timur.audio/how-to-make-a-container-from-a-c20-range

apolukhin commented 3 years ago

Комитет решил идти немного другим путём, через ranges::to https://wg21.link/p1206

apolukhin commented 2 years ago

Приняли в C++23 вместе с новыми конструкторами от диапазона для контейнеров https://wg21.link/p1206