std::views::filter и const
От: sergii.p  
Дата: 30.11.20 17:48
Оценка:
всем привет.
Натолкнулся на довольно странную ошибку. Не понимаю, почему она возникает

#include <iostream>
#include <ranges>

int main()
{
    /*const*/ auto odds = std::views::iota(1)
        | std::views::filter([](int i){ return i % 2 == 0; })
        | std::views::take(100);
    for(const auto i: odds) {
        std::cout << i << std::endl;
    }
}


этот код успешно компилируется. Но если раскомментировать const, то получаю ошибку

error: passing 'const std::ranges::take_view<std::ranges::filter_view<std::ranges::iota_view<int, std::unreachable_sentinel_t>, main()::<lambda(int)> > >' as 'this' argument discards qualifiers [-fpermissive]
25 | for(const auto i: odds) {


Почему так происходит? gcc 11.0.0.20201
Re: std::views::filter и const
От: σ  
Дата: 30.11.20 19:07
Оценка:
Дальше же в сообщении компилятора написано, что методы `begin`/`end` не const-qualified.
Re[2]: std::views::filter и const
От: sergii.p  
Дата: 01.12.20 07:12
Оценка:
Здравствуйте, σ, Вы писали:

σ>Дальше же в сообщении компилятора написано, что методы `begin`/`end` не const-qualified.


вопрос — почему. Что такого делает filter, что методы не могут быть const
Re[3]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 08:37
Оценка:
Здравствуйте, sergii.p, Вы писали:

σ>>Дальше же в сообщении компилятора написано, что методы `begin`/`end` не const-qualified.


SP>вопрос — почему. Что такого делает filter, что методы не могут быть const


Так odds -- это же не filter, а take_view. И take_view должен, как минимум, считать, сколько элементов уже было взято из range, над которым работает take_view. Это мутабельная операция.

Ваш К.О.

UPD. Похоже, был не прав. Приношу свои извинения.
Отредактировано 01.12.2020 16:33 so5team . Предыдущая версия .
Re[4]: std::views::filter и const
От: Chorkov Россия  
Дата: 01.12.20 09:27
Оценка:
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, sergii.p, Вы писали:


σ>>>Дальше же в сообщении компилятора написано, что методы `begin`/`end` не const-qualified.


SP>>вопрос — почему. Что такого делает filter, что методы не могут быть const


S>Так odds -- это же не filter, а take_view. И take_view должен, как минимум, считать, сколько элементов уже было взято из range, над которым работает take_view. Это мутабельная операция.


S>Ваш К.О.


Как ни странно, take — работает: https://wandbox.org/permlink/Y74CJT4bN1PFMRRF,
а filter — нет: https://wandbox.org/permlink/aI2gSYhTIwn36BiX

Re[4]: std::views::filter и const
От: cockRoach Австрия  
Дата: 01.12.20 10:17
Оценка:
S>Так odds -- это же не filter, а take_view. И take_view должен, как минимум, считать, сколько элементов уже было взято из range, над которым работает take_view. Это мутабельная операция.
как чтение количества — mutable???
Re[5]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 12:46
Оценка:
Здравствуйте, cockRoach, Вы писали:

S>>Так odds -- это же не filter, а take_view. И take_view должен, как минимум, считать, сколько элементов уже было взято из range, над которым работает take_view. Это мутабельная операция.

R>как чтение количества — mutable???

Какого количества?
Re[5]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 12:57
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Как ни странно, take — работает: https://wandbox.org/permlink/Y74CJT4bN1PFMRRF,

C>а filter — нет: https://wandbox.org/permlink/aI2gSYhTIwn36BiX

В реализации take_view видны две версии end(): одна для случая сложного вложенного view, вторая для случая простого view
https://github.com/gcc-mirror/gcc/blob/d66db7412ee8e16e08b340767f4c00a3b570e730/libstdc%2B%2B-v3/include/std/ranges#L1763-L1789
Когда задействуется filter_view, то выбирается неконстантный end, т.к. filter_view, видимо, к простым view не относится.

Я удивлен тому, что для простого, но неограниченного в размере, view выбирается константный end. Там же вроде как тоже надо считать количество извлеченных элементов.
Отредактировано 01.12.2020 13:01 so5team . Предыдущая версия .
Re[6]: std::views::filter и const
От: cockRoach Австрия  
Дата: 01.12.20 14:38
Оценка:
S>>>Так odds -- это же не filter, а take_view. И take_view должен, как минимум, считать, сколько элементов уже было взято из range, над которым работает take_view. Это мутабельная операция.
R>>как чтение количества — mutable???

S>Какого количества?

take_view должен... считать, сколько элементов уже было взято из range... Это мутабельная операция.

Re[7]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 14:49
Оценка:
Здравствуйте, cockRoach, Вы писали:

R>>>как чтение количества — mutable???


S>>Какого количества?

R>

R>take_view должен... считать, сколько элементов уже было взято из range... Это мутабельная операция.


Так это количество нужно считать, а не читать. Тут же мы имеем iota в основе, т.е. range не фиксированного размера. Поэтому take_view не может сразу взять и вычислить два итератора begin/end, которые можно будет сравнивать друг с другом. А придется считать взятые из лежащих ниже range-й элементы при каждом новом чтении.

Ну или поясните свое "чтение количества" чтобы у всех было одинаковое понимание предмета разговора.
Re[8]: std::views::filter и const
От: sergii.p  
Дата: 01.12.20 15:24
Оценка:
Здравствуйте, so5team, Вы писали:

S>Так это количество нужно считать, а не читать. Тут же мы имеем iota в основе, т.е. range не фиксированного размера. Поэтому take_view не может сразу взять и вычислить два итератора begin/end, которые можно будет сравнивать друг с другом. А придется считать взятые из лежащих ниже range-й элементы при каждом новом чтении.


никто не считает end. end — это логическое окончание. Он может быть вообще один для всех коллекций. Его не надо вычислять. Как вариант, можно взять end у "родительского" рэнжа. И опять же никто ничего не меняет и не вычисляет.
begin тоже никто не считает. Просто берут begin у "родительской" коллекции.
Уже ведь писали что iota | take прекрасно иммутабельно работает.
Re[6]: std::views::filter и const
От: Chorkov Россия  
Дата: 01.12.20 15:31
Оценка:
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, Chorkov, Вы писали:


C>>Как ни странно, take — работает: https://wandbox.org/permlink/Y74CJT4bN1PFMRRF,

C>>а filter — нет: https://wandbox.org/permlink/aI2gSYhTIwn36BiX

S>В реализации take_view видны две версии end(): одна для случая сложного вложенного view, вторая для случая простого view

S>https://github.com/gcc-mirror/gcc/blob/d66db7412ee8e16e08b340767f4c00a3b570e730/libstdc%2B%2B-v3/include/std/ranges#L1763-L1789
S>Когда задействуется filter_view, то выбирается неконстантный end, т.к. filter_view, видимо, к простым view не относится.

S>Я удивлен тому, что для простого, но неограниченного в размере, view выбирается константный end. Там же вроде как тоже надо считать количество извлеченных элементов.


Счетчик должен находиться в итераторе, а не во view.
Иначе, нельзя иметь два итератора к одной view:
    const auto odds =std::views::iota(1)  | std::views::take(3);
    for(const auto i: odds1) 
        for(const auto j: odds1) 
            std::cout << i << j << std::endl;
Re[9]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 15:41
Оценка:
Здравствуйте, sergii.p, Вы писали:

S>>Так это количество нужно считать, а не читать. Тут же мы имеем iota в основе, т.е. range не фиксированного размера. Поэтому take_view не может сразу взять и вычислить два итератора begin/end, которые можно будет сравнивать друг с другом. А придется считать взятые из лежащих ниже range-й элементы при каждом новом чтении.


SP>никто не считает end. end — это логическое окончание. Он может быть вообще один для всех коллекций. Его не надо вычислять. Как вариант, можно взять end у "родительского" рэнжа. И опять же никто ничего не меняет и не вычисляет.

SP>begin тоже никто не считает. Просто берут begin у "родительской" коллекции.

Тогда расскажите мне, как должен работать take_view.

Вот если в take_view засовывают range фиксированного размера, то take_view сразу может высчитать значения begin и end: тупо begin_=range.begin(), end_=advance(range.end(), min(range.size(),take_size)). После чего экземпляр take_view будет тупо возвращать свои begin_ и end_ из begin() const и end() const.

А вот если в take_view засовывают безразмерный range, то как take_view должен брать begin (хотя это не должно быть сложно) и, в особенности end?

Ведь если взяли b=take_view.begin(), а потом сделали ++b, то при сравнении нового значения с take_view.end() как-то нужно определить, извлекли ли уже из range все положенные элементы или нет. И откуда это знание возьмется?

SP>Уже ведь писали что iota | take прекрасно иммутабельно работает.


Это вот как раз меня сильно удивляет. В отличии от случая с filter.
Re[3]: std::views::filter и const
От: vopl Россия  
Дата: 01.12.20 15:49
Оценка: 26 (4)
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, σ, Вы писали:


σ>>Дальше же в сообщении компилятора написано, что методы `begin`/`end` не const-qualified.


SP>вопрос — почему. Что такого делает filter, что методы не могут быть const


https://en.cppreference.com/w/cpp/ranges/filter_view
std::ranges::filter_view::begin
...
this function caches the result within the filter_view object for use on subsequent calls.

Поэтому ему нужен неконстантный объект.

[добавлено позже] Если б я был султан — я б для этого кэша запользовал mutable вместо того чтобы делать неконстантные методы
Отредактировано 01.12.2020 15:55 vopl . Предыдущая версия .
Re[7]: std::views::filter и const
От: so5team https://stiffstream.com
Дата: 01.12.20 16:17
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Счетчик должен находиться в итераторе, а не во view.

C>Иначе, нельзя иметь два итератора к одной view:
C>
C>    const auto odds =std::views::iota(1)  | std::views::take(3);
C>    for(const auto i: odds1) 
C>        for(const auto j: odds1) 
C>            std::cout << i << j << std::endl;
C>


Интересно. Но тут счетчиком как бы и не ограничится. Такое ощущение, что для каждого итератора своя копия исходного iota_view создается.
Re: std::views::filter и const
От: swingus  
Дата: 26.12.20 18:39
Оценка:
Мне контрибьютор ranges.v3 сказал, что это by design — views могут быть константными и неконстантными — это зависит от реализации, никаких ограничений на стандартные view не накладывается. Видимо, следует изменить свои привычки и не делать переменные типа view константными.

Здравствуйте, sergii.p, Вы писали:
SP>Почему так происходит? gcc 11.0.0.20201
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.