JohnB
JohnB

Reputation: 13713

How to determine the iterator type for container?

What do I have to write for YYY, ZZZ in order to set iterator_type to the iterator type associated with T ? It should work in Visual Studio C++ 2010, if possible (but general standard solution is ok as well).

template<class T>
struct iterator_for {
    typedef YYY<T>::ZZZ type;
}

Hence I want:

iterator_for<double[3]>::type is double *
iterator_for<std::string>::type is std::string::iterator
iterator_for<char[12]>::type is char *

etc.

I have a templated wrapper class Wrapper<T> storing something iterable (i.e. a container or string or an array) and I want to define a function returning an iterator pointing into the wrapped object. For that purpose, I need to be able to speak about the iterator type corresponding to T. For an array, the corresponding iterator would be a pointer, and for a string that is whatever string defines to be its iterator type.

Upvotes: 3

Views: 11010

Answers (4)

haslersn
haslersn

Reputation: 585

My solution is:

typedef decltype(std::begin(std::declval<T&>())) type;

The iterator type is the type returned by std::begin when called on an instance of T. std::declval<T&> is declared to return a T&, on which we can call std::begin.

This differs from JohnB's answer in that we pass a reference to the container type to std::begin. This is needed because:

  • std::declval<T>() returns an rvalue reference (T&&).
  • std::begin takes its argument by non-const reference.
  • A non-const reference can't bind to a temporary.

std::declval<T&> however returns an lvalue refernce because of reference collapsing.

Upvotes: 7

Ross Smith
Ross Smith

Reputation: 3765

The Boost library already has this:

#include <boost/range.hpp>
#include <iostream>
#include <string>
#include <vector>

template <typename T> void print_list(const T& container) {
    typedef typename boost::range_iterator<const T>::type iter;
    for (iter i = boost::begin(container); i != boost::end(container); ++i)
        std::cout << *i << ";";
    std::cout << "\n";
}

int main() {
    double array[] = {1.0,2.0,3.0};
    std::string str = "Hello";
    std::vector<int> vec(3, 10);
    print_list(array);  // prints 1;2;3;
    print_list(str);    // prints H;e;l;l;o;
    print_list(vec);    // prints 10;10;10;
}

Upvotes: 1

Bo Persson
Bo Persson

Reputation: 92271

If you just want to separate containers from pointers, you can try this

template<class T>
struct iterator_for 
{
    typedef typename T::iterator  type;
};

template<class T>
struct iterator_for<T*>
{
    typedef T*  type;
};

template<class T, std::size_t N>
struct iterator_for<T (&)[N]>
{
    typedef T*  type;
};

Upvotes: 4

JohnB
JohnB

Reputation: 13713

Ok, one possibility probably is (in C++11, but does not work in VS 2010):

typedef typename std::remove_reference<
                    decltype ( 
                        begin ( std::declval<T> () )
                    )
                 >::type
        type;

Upvotes: 3

Related Questions