jotik
jotik

Reputation: 17920

std::iterator_traits divergence between libstdc++ and libc++

Given:

struct Iter {
    using value_type = int;
    using difference_type = int;
    using reference = int;
    using pointer = int;
    using iterator_category = int;
};

The following works fine with libstc++, but fails to compile against libc++ 5.0.0:

#include <iterator>
#include <type_traits>
static_assert(
    std::is_same<
        std::iterator_traits<Iter>::iterator_category,
        Iter::iterator_category
    >::value, "");

With the error:

error: no member named 'iterator_category' in 'std::__1::iterator_traits<Iter>' std::is_same<std::iterator_traits<Iter>::iterator_category, Iter::iterator_category>::value, "");

The static assertion succeeds if Iter::iterator_category is one of the standard input categories, e.g. std::input_iterator_tag.

IMHO it shouldn't fail, because the C++ draft states in [iterator.traits]#2:

If Iterator has valid ([temp.deduct]) member types difference_­type, value_­type, pointer, reference, and iterator_­category, iterator_­traits<Iterator> shall have the following as publicly accessible members:

using difference_type   = typename Iterator::difference_type;
using value_type        = typename Iterator::value_type;
using pointer           = typename Iterator::pointer;
using reference         = typename Iterator::reference;
using iterator_category = typename Iterator::iterator_category;

Otherwise, iterator_­traits<Iterator> shall have no members by any of the above names.

Can anybody please explain whether this is an implementation bug, or why my expectations are wrong?

Upvotes: 4

Views: 263

Answers (1)

Barry
Barry

Reputation: 303517

We also have, in [std.iterator.tags]:

It is often desirable for a function template specialization to find out what is the most specific category of its iterator argument, so that the function can select the most efficient algorithm at compile time. To facilitate this, the library introduces category tag classes which are used as compile time tags for algorithm selection. They are: input_­iterator_­tag, output_­iterator_­tag, forward_­iterator_­tag, bidirectional_­iterator_­tag and random_­acces_­iterator_­tag. For every iterator of type Iterator, iterator_­traits<Iterator>​::​iterator_­category shall be defined to be the most specific category tag that describes the iterator's behavior.

namespace std {
  struct input_iterator_tag { };
  struct output_iterator_tag { };
  struct forward_iterator_tag: public input_iterator_tag { };
  struct bidirectional_iterator_tag: public forward_iterator_tag { };
  struct random_access_iterator_tag: public bidirectional_iterator_tag { };
}

int isn't one of those tags, so iterator_traits<Iter>::iterator_category can't give you back int. I would suggest that having an invalid iterator category is simply violating the preconditions of iterator_traits - this doesn't necessarily mean that the library must fail, but it also doesn't mean that failure is a library bug.

However, these preconditions aren't spelled out as explicitly in [iterators] as they are in other parts of the library section. So I'd be inclined to suggest that both libraries are correct, but libc++'s approach to not defining any member aliases in iterator_traits<Iter> is likely better.

Upvotes: 2

Related Questions