Run-time Polymorphism with Ranges

Understand how run-time polymorphism can be achieved using inputRangeObject() and outputRangeObject().

Run-time polymorphism with inputRangeObject() and outputRangeObject()

Being implemented mostly as templates, ranges exhibit compile-time polymorphism, which we have been taking advantage of in the examples of this chapter and previous chapters. (For differences between compile-time polymorphism and run-time polymorphism, see the “Compile-time polymorphism” section in the more templates chapter.)

Compile-time polymorphism has to deal with the fact that every instantiation of a template is a different type. For example, the return type of the take() template is directly related to the original range:

writeln(typeof([11, 22].negative.take(1)).stringof); 
writeln(typeof(FibonacciSeries().take(1)).stringof);

The output:

Take!(Negative!(int[]))
Take!(FibonacciSeries)

A natural consequence of this fact is that different range types cannot be assigned to each other. The following is an example of this incompatibility between two InputRange ranges:

auto range = [11, 22].negative;
// ... at a later point ...
range = FibonacciSeries(); // ← compilation ERROR

As expected, the compilation error indicates that FibonacciSeries and Negative!(int[]) are not compatible:

Error: cannot implicitly convert expression (FibonacciSeries(0, 1))
of type FibonacciSeries to Negative!(int[])

However, although the actual types of the ranges are different since they both are ranges of int, this incompatibility can be seen as an unnecessary limitation. From the usage point of view, since both ranges simply provide int elements, the actual mechanism that produces those elements should not be important.

Phobos helps with this issue by inputRangeObject() and outputRangeObject(). inputRangeObject() allows presenting ranges as a specific kind of range of specific types of elements. With its help, a range can be used e.g. as an InputRange of int elements, regardless of the actual type of the range.

inputRangeObject() is flexible enough to support all of the non-output ranges: InputRange, ForwardRange, BidirectionalRange, and RandomAccessRange. Due to this flexibility, the object that it returns cannot be defined by auto. The exact kind of range that is required must be specified explicitly:

Get hands-on with 1200+ tech skills courses.