When reading the excerpt from cppreference
If
Iterator
does not have the five member typesdifference_type
,value_type
,pointer
,reference
, anditerator_category
, then this template has no members by any of those names (std::iterator_traits
is SFINAE-friendly)
I automatically thought it meant each member type is defined when they are defined in the iterator itself. But lo and behold, it actually meant if all five are defined, then they are defined.
struct defined
{
using difference_type = int;
using value_type = int;
using pointer = int*;
using reference = int&;
using iterator_category = std::input_iterator_tag;
};
struct undefined
{
using value_type = int;
};
template<typename T>
using value_type = typename std::iterator_traits<T>::value_type;
void foo()
{
using std::experimental::is_detected_v;
static_assert(is_detected_v<value_type, defined>);
static_assert(!is_detected_v<value_type, undefined>);
}
Why is this? I would've thought it is friendlier if they were independent of each other. For example if an algorithm just needs to store the value_type
somewhere and doesn't care about anything else.
template<typename It>
auto amazingfy(It first, It last)
{
typename std::iterator_traits<It>::value_type v;
for(; first != last; first++)
v += *first;
return v;
}
It will fail to compile on some iterator that only defined value_type
, but funnily enough, succeed if it were instead typename It::value_type v;