Чи можу я повернути тимчасовий трубопровід до дальності дії?


9

Припустимо, у мене є generate_my_rangeклас, який моделює range(зокрема, є regular). Тоді правильний наступний код:

auto generate_my_range(int some_param) {    
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  return my_custom_rng_gen(some_param) | ranges::views::transform(my_transform_op);
}
auto cells = generate_my_range(10) | ranges::to<std::vector>;

Чи my_custom_rng_gen(some_param)приймається за значенням оператором (першим) труби, або я маю звисаючу посилання, коли я виходжу з generate_my_rangeобласті застосування?

Чи було б те саме з функціональним викликом ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)?

Чи було б правильно, якби я використав посилання на значення? наприклад:

auto generate_my_range(int some_param) {
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  auto tmp_ref = my_custom_rng_gen(some_param);
  return tmp_ref | ranges::views::transform(my_transform_op);
}

Якщо діапазони приймаються значеннями для цих операцій, то що робити, якщо я передаю посилання на значення в контейнер? Чи варто використовувати ranges::views::all(my_container)візерунок?


Чи вже обмежений my_custom_rng_gen (some_param)? Ви маєте на увазі щось на кшталт godbolt.org/z/aTF8RN без прийому (5)?
Porsche9II

@ Porsche9II Так, це обмежений діапазон. Скажімо, це контейнер
Беренгер,

Відповіді:


4

У бібліотеці діапазонів є два види операцій:

  • погляди, які ліниві і вимагають існування базового контейнера.
  • дії, які прагнуть, і в результаті створюють нові контейнери (або змінюють існуючі)

Погляди легкі. Ви передаєте їх за значенням і вимагаєте, щоб базові контейнери залишалися дійсними та незмінними.

З документації на діапазони-v3

Вид - це легка обгортка, яка представляє вигляд основної послідовності елементів певним чином, не мутуючи і не копіюючи її. Погляди дешеві для створення та копіювання та мають невласницьку довідкову семантику.

і:

Будь-яка операція в нижньому діапазоні, що визнає недійсним її ітераторів або дозорних, також призведе до недійсності будь-якого представлення, яке стосується будь-якої частини цього діапазону.

Знищення базового контейнера очевидно збиває з нього всіх ітераторів.

У своєму коді ви спеціально використовуєте представлення даних - Ви використовуєтеranges::views::transform . Труба - це лише синтаксичний цукор, який дозволяє легко писати так, як є. Вам слід подивитися на останнє, що знаходиться в трубі, щоб побачити, що ви виробляєте - у вашому випадку це вид.

Якби не було трубного оператора, це, мабуть, виглядало б приблизно так:

ranges::views::transform(my_custom_rng_gen(some_param), my_transform_op)

якби було пов'язано кілька перетворень таким чином, ви можете бачити, наскільки потворним це було б.

Таким чином, якщо my_custom_rng_genвиробляється якийсь контейнер, який ви трансформуєте і потім повертаєте, той контейнер знищується, і у вас є звисаючі посилання зі свого погляду. Якщо my_custom_rng_genінший вид на контейнер, який живе за межами цих областей, все добре.

Однак компілятор повинен бути в змозі визнати, що ви застосовуєте подання на тимчасовий контейнер і вдарив вас про помилку компіляції.

Якщо ви хочете, щоб ваша функція повертала діапазон як контейнер, потрібно чітко "матеріалізувати" результат. Для цього використовуйте ranges::toоператор у межах функції.


Оновлення: щоб бути більш реплікованим щодо Вашого коментаря, "де в документації написано, що діапазон складання / трубопровід займає і зберігає вигляд?"

Труба - це просто синтаксичний цукор для з'єднання речей у легкому для читання виразі. Залежно від способу його використання, він може або не може повернути погляд. Це залежить від правого аргументу. У вашому випадку це:

`<some range> | ranges::views::transform(...)`

Отже вираз повертає те, що views::transformповертається.

Тепер, прочитавши документацію перетворення:

Нижче наводиться перелік комбінаторів лінивого діапазону чи виду, які надає Range-v3, і розмиття про те, як кожен з них планується використовувати.

[...]

views::transform

Враховуючи діапазон джерела та одинарну функцію, поверніть новий діапазон, коли кожен елемент результату є результатом застосування одинарної функції до елемента джерела.

Таким чином, він повертає діапазон, але оскільки це ледачий оператор, цей діапазон, який він повертає, - це погляд, з усією його семантикою.


Добре. Що для мене все ще трохи загадкове, як це працює, коли я передаю контейнер до труби (тобто об'єкт діапазону, створений композицією). Для цього потрібно якось зберігати вигляд контейнера. Це робиться з ranges::views::all(my_container)? А що робити, якщо вигляд передається трубі? Чи розпізнає він переданий контейнер або перегляд? Це потрібно? Як?
Беренгер

"Компілятор повинен бути в змозі розпізнати, що ви застосовуєте подання на тимчасовий контейнер і вдарив вас про помилку компіляції" Це те, що я подумав теж: якщо я роблю щось дурне, це означає контракт типу (бути лівою значення) не виконується. Такі речі роблять діапазон-v3. Але в цьому випадку проблем абсолютно немає. Він компілює І працює. Тож може бути невизначена поведінка, але вона не проявляється.
Беренгер

Щоб переконатися, що ваш код працює випадково або якщо все добре, мені потрібно буде переглянути вміст my_custom_rng_gen. Як саме труба і transformвзаємодіють під витяжкою, не важливо. Весь вираз приймає діапазон як аргумент (контейнер або вигляд до якогось контейнера) і повертає інший вигляд до цього контейнера. Повернене значення ніколи не буде володіти контейнером, оскільки це перегляд.
CygnusX1

1

Взяте з документації на діапазони-v3 :

Погляди [...] мають невласне опорну семантику.

і

Наявність об'єкта єдиного діапазону дозволяє здійснювати трубопроводи операцій. У трубопроводі діапазон ліниво адаптується або нетерпляче мутується певним чином, результат одразу доступний для подальшої адаптації або мутації. Ледача адаптація обробляється поглядами, а жадібна мутація обробляється діями.

// taken directly from the the ranges documentation
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){ return i % 2 == 1; })
              | views::transform([](int i){ return std::to_string(i); });
// rng == {"2","4","6","8","10"};

У наведеному вище коді rng просто зберігає посилання на основні дані та функції фільтра та перетворення. Жодна робота не виконується, доки ініціюється rng.

Оскільки ви сказали, що тимчасовий діапазон можна розглядати як контейнер, ваша функція повертає звисаючу посилання.

Іншими словами, вам потрібно переконатися, що нижній діапазон переживає погляд, або ви перебуваєте у біді.


Так, перегляди не є власниками, але де в документації написано, що діапазон складання / трубопровід займає та зберігає вигляд? Можливо (і я вважаю, що непогано) мати таку політику: зберігати за значенням, якщо діапазон задається рецензією на значення.
Беренгер

1
@ Bérenger Я додав ще трохи з документації про діапазони. Але справа справді в цьому: погляд не є власником . Байдуже, чи передаєте ви це ревальвінг.
Румбурак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.