c++booststltype-erasureboost-range

What are the benefits of using boost::any_range?


What are the benefits of using boost::any_range? Here is an example:

typedef boost::any_range<
    int
  , boost::forward_traversal_tag
  , int
  , std::ptrdiff_t
> integer_range;

void display_integers(const integer_range& rng)
{
    boost::copy(rng,
                std::ostream_iterator<int>(std::cout, ","));

    std::cout << std::endl;
}

int main(){
    std::vector<int> input{ ... };
    std::list<int> input2{ ... };
    display_integers(input);
    display_integers(input2);
}

But the same functionality with more efficiency can be achieved with a template parameter, which satisfies the ForwardRange concept:

template <class ForwardRange>
void display_integers(const ForwardRange& rng)
{
    boost::copy(rng,
                std::ostream_iterator<int>(std::cout, ","));

    std::cout << std::endl;
}

So I am searching for scenarios when it is worth to use any_range. Maybe I am missing something.


Solution

  • This technique is called Type Erasure. There is a full article describing the pros and cons on the example of any_iterator: On the Tension Between Object-Oriented and Generic Programming in C++.

    It is possible to hide (in a separate file/library) the implementation/definition of

    void display_integers(const integer_range& rng)
    

    But in the case of

    template <class ForwardRange>
    void display_integers(const ForwardRange& rng)
    

    you have to provide source code to users (or at least make explicit instantiations somewhere).

    Moreover, in the first case, display_integers will be compiled only once, but in the second it will be compiled for every type of the passed range.

    Also, you may have somewhere

    integer_range rng;
    

    and during lifetime of rng you may assign ranges of different types to it:

    vector<int> v;
    list<int> l;
    integer_range rng;
    rng = v;
    rng = l;
    

    The biggest disadvantage of type erasure is its runtime cost; all operations are virtual, and cannot be inlined (easily).


    P.S. another famous example of type erasure is std::function