I have two and a half closely related questions. Given an STL iterator-type passed as a template parameter:
enable_if
s 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