Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check/modify iterator "constness"

I have two and a half closely related questions. Given an STL iterator-type passed as a template parameter:

  1. How to determine whether the type corresponds to a const- or non-const iterator?
  2. Alternatively to 1., how to impose (using enable_ifs for instance) that this type corresponds to a non-const iterator?
  3. How to obtain the const- version of the iterator from the non-const one (and vice versa)? [ Note: answered in this post; not surprisingly, you can't. ]

Where does this question come from:

I wrote a small class to facilitate arithmetic/relational/algebraic operations on vectors (by vector I mean 1d fixed-size data, not the STL vectors). Instead of imposing a specific data container, I've defined an interface and derived several possible containers that are basically "wrapping" various ways of storing data. One of these containers is a wrapper for STL-iterators, and I'm having some troubles with it.

like image 237
Jonathan H Avatar asked May 11 '13 15:05

Jonathan H


2 Answers

Question 1:

You could use the following type trait:

template<typename T, typename = void>
struct is_const_iterator : std::false_type { };

template<typename T>
struct is_const_iterator<T,
    typename std::enable_if<
        std::is_const<
            typename std::remove_pointer<
                typename std::iterator_traits<T>::pointer
                >::type
            >::value
        >::type> : std::true_type { };

Here is a demonstration:

#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template<typename T, typename = void>
struct is_const_iterator : std::false_type { };

template<typename T>
struct is_const_iterator<T,
    typename std::enable_if<
        std::is_const<
            typename std::remove_pointer<
                typename std::iterator_traits<T>::pointer
                >::type
            >::value
        >::type> : std::true_type { };

int main()
{
    typedef std::list<int>::iterator LI;
    typedef std::list<int>::const_iterator CLI;
    static_assert(is_const_iterator<LI>::value, "!"); // Fires
    static_assert(is_const_iterator<CLI>::value, "!"); // Does not fire

    typedef std::vector<int>::iterator VI;
    typedef std::vector<int>::const_iterator CVI;
    static_assert(is_const_iterator<VI>::value, "!"); // Fires
    static_assert(is_const_iterator<CVI>::value, "!"); // Does not fire
}

And here is a live example.

Question 2:

With the above type trait, this becomes simple. Suppose you have a function template foo() that you want to constrain so that it accepts only non-const iterators:

template<typename It,
    typename std::enable_if<!is_const_iterator<It>::value>::type* = nullptr>
void foo(It i)
{
    // Does something with i...
}

And a simple demonstration program:

int main()
{
    std::vector<int> v;
    foo(v.begin()); // OK
    foo(v.cbegin()); // ERROR!
}

And here is a live example.

like image 77
Andy Prowl Avatar answered Sep 28 '22 01:09

Andy Prowl


For 1), you could do something like this:

std::is_const<
  typename std::remove_reference<
    typename std::iterator_traits<Iterator>::reference
  >::type
>::value

Or this:

std::is_const<
  typename std::remove_reference<
    decltype(*iterator)
  >::type
>::value

You can use these predicates to pass to std::enable_if to implement 2).

NOTE: As pointed out by R. Martinho Fernandes in the comments, these predicates will fail if the iterator in question uses a different type than plain references for its reference trait (such as std::vector<bool>::const_iterator does).

like image 29
Angew is no longer proud of SO Avatar answered Sep 28 '22 01:09

Angew is no longer proud of SO