Предположим, у меня есть 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)
?
Было бы правильно, если бы я использовал ссылку lvalue? например:
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);
}
Если диапазоны взяты значениями для этих операций, что мне делать, если я передаю ссылку lvalue в контейнер? Должен ли я использовать ranges::views::all(my_container)
шаблон?
Ответы:
В библиотеке диапазонов есть два вида операций:
Виды легкие. Вы передаете их по значению и требуете, чтобы нижележащие контейнеры оставались действительными и неизменными.
Из документации диапазонов-v3
а также:
Разрушение нижележащего контейнера, очевидно, делает недействительными все итераторы к нему.
В вашем коде вы специально используете представления - вы используете
ranges::views::transform
. Труба - это просто синтаксический сахар, который позволяет легко писать, как она есть. Вы должны взглянуть на последнюю вещь в трубе, чтобы увидеть, что вы производите - в вашем случае это представление.Если бы не было оператора канала, он бы выглядел примерно так:
если бы было несколько преобразований, связанных таким образом, вы можете увидеть, насколько уродливым это будет.
Таким образом, если
my_custom_rng_gen
вы создаете какой-то контейнер, который вы трансформируете, а затем возвращаете, этот контейнер уничтожается, и у вас есть свисающие ссылки из вашего представления. Еслиmy_custom_rng_gen
есть другой взгляд на контейнер, который живет за пределами этих областей, все в порядке.Однако компилятор должен быть в состоянии распознать, что вы применяете представление к временному контейнеру, и ударил вас с ошибкой компиляции.
Если вы хотите, чтобы ваша функция возвращала диапазон в виде контейнера, вам необходимо явно «материализовать» результат. Для этого используйте
ranges::to
оператор внутри функции.Обновление: чтобы быть более точным в отношении вашего комментария "где документация говорит, что составление диапазона / трубопровода берет и сохраняет представление?"
Труба - просто синтаксический сахар, чтобы соединить вещи в легко читаемом выражении. В зависимости от того, как он используется, он может возвращать или не возвращать представление. Это зависит от аргумента правой части. В вашем случае это:
Таким образом, выражение возвращает все, что
views::transform
возвращает.Теперь, читая документацию о преобразовании:
Таким образом, он возвращает диапазон, но поскольку это ленивый оператор, этот диапазон, который он возвращает, является представлением со всей его семантикой.
источник
ranges::views::all(my_container)
? А что, если представление передается в трубу? Распознает ли он, что передан контейнер или представление? Это нужно? Как?my_custom_rng_gen
. Как именно труба и какtransform
взаимодействуют под капотом, не важно. Целое выражение принимает диапазон в качестве аргумента (контейнер или представление некоторого контейнера) и возвращает другое представление этому контейнеру. Возвращаемое значение никогда не будет владеть контейнером, потому что это представление.Взято из документации диапазонов-v3 :
а также
Поскольку вы сказали, что временный диапазон можно рассматривать как контейнер, ваша функция возвращает висячую ссылку.
Другими словами, вам нужно убедиться, что базовый диапазон переживает представление, или у вас проблемы.
источник