В библиотеке диапазонов есть два вида операций:
- представления, которые ленивы и требуют, чтобы основной контейнер существовал.
- действия, которые стремятся, и производят новые контейнеры в результате (или изменяют существующие)
Виды легкие. Вы передаете их по значению и требуете, чтобы нижележащие контейнеры оставались действительными и неизменными.
Из документации диапазонов-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
Если задан диапазон источника и унарная функция, верните новый диапазон, где каждый элемент результата является результатом применения унарной функции к элементу источника.
Таким образом, он возвращает диапазон, но поскольку это ленивый оператор, этот диапазон, который он возвращает, является представлением со всей его семантикой.