The naive implementation of range would have a range represented of a pairs of iterators, with every iterator containing a copy of each upstream range, which blows up quadratically; libstdc++ implementation is not implemented this way though: each range has one copy of the upstream range plus the predicate, while this could be optimized further for stateless predicates, the range growth should still be linear.
Yet, when I print the final size of the chained filter ranges and I saw it increasing superlinearly with the number of stages, so I must be missing something.
There is also additional wasted stack space for each temporary range. In theory the compiler should be able to optimize them away, but it probably doesn't always.