views:

429

answers:

4

I have a container filled with pairs. I want to iterate in it using the STL generic algorithms (in my case it would be inner_product, but consider it as a generic problem). The algorithm I am using expects iterators first and last. Can I provide special iterators first and last that will iterate not on the pairs but on the first element of each pair?

I know i can do it manually, providing a hand-made function object that will be a wrapper around the standard container iterator, deferencing it to the first member of the pair intend of the pair itself,but I think there is also a clever one-liner to do it for me. What would it be?

A: 

You can subclass e.g. std::vector::const_iterator yourself, reimplementing operator* and operator-> to return the first of the pair. You'd also need to create your own begin() and end() functions to return your custom iterator.

You can also create to binary function classes and pass those to inner_product.

strager
Are you sure i can subclass stl iterators? I always though that they provided no virtual destrucor, not enabling subclassing.
David Reis
I didn't say subclass std::iterator. I said subclass std::vector::const_iterator. I tried it just now, and it works.
strager
you *can* (sorry my comment said "cannot", i indeed meant "can") subclass from std::iterator indeed. it provides the common typedefs needed. its purpose is not to provide a polymorphic interface
Johannes Schaub - litb
A: 

There is no clever one-liner solution. Your best hope is to write a wrapper iterator. This is actually a pretty canonical solution. You might check whether Boost already has what you need. If not, try to write a generic wrapper that can be reused for other problems.

The STL contains such an iterator wrapper called reverse_iterator. The name implies its use.

Konrad Rudolph
+8  A: 

I've looked around and found boost::transform_iterator. I've come up with this code. Surprising how well it works:

#include <map>
#include <algorithm>
#include <iostream>
#include <string>
#include <iterator>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

int main() {
    typedef std::map<std::string, int>::value_type value_type;
    std::map<std::string, int> a;
    a["one"] = 1;
    a["two"] = 2;

    // returns the second element 
    boost::function<int(value_type&)> f = boost::bind(&value_type::second, _1);
    std::copy(boost::make_transform_iterator(a.begin(), f), 
              boost::make_transform_iterator(a.end(), f),
              std::ostream_iterator<int>(std::cout, " "));

}

It's printing "1 2 " to the standard output.

Johannes Schaub - litb
It is as close to a "clever one liner solution" as I think we will get... :-)
David Reis
+1: too late to answer....
Nicola Bonelli
you *could* put everything into one line. but you would have to repeat the bind call. ugly code copy'n'paste then :)
Johannes Schaub - litb
A: 

Ultimately, I think your idea is the way to go. You can use Boost to help you do it. To start, you'd need a function that takes your pair and returns the first element. I think you could write such a function in-line using the Lambda library, but for readability's sake, I think I'd just write a simple function that does that instead. Then pass that function with your original iterators to construct a transform_iterator for your sequence's begin and end.

Rob Kennedy