I have two and a half closely related questions. Given an STL iterator-type passed as a template parameter:
enable_ifs for instance) that this type corresponds to a non-const iterator?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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With