Why is std::iterator deprecated? - c++

Template class std::iterator is set to be deprecated in C++17. Why so? It has been a handy way to make sure std::iterator_traits works, especially if you can make use of the default template arguments. Is there some other way of doing it in C++17?

From the proposal that suggested its deprecation:
As an aid to writing iterator classes, the original standard library supplied the iterator class template to automate the declaration of the five typedefs expected of every iterator by iterator_traits. This was then used in the library itself, for instance in the specification of std::ostream_iterator:
template <class T, class charT = char, class traits = char_traits<charT> >
class ostream_iterator:
public iterator<output_iterator_tag, void, void, void, void>;
The long sequence of void arguments is much less clear to the reader than simply providing the expected typedefs in the class definition itself, which is the approach taken by the current working draft, following the pattern set in C++14 where we deprecated the derivation throughout the library of functors from unary_function and binary_function.
In addition to the reduced clarity, the iterator template also lays a trap for the unwary, as in typical usage it will be a dependent base class, which means it will not be looking into during name lookup from within the class or its member functions. This leads to surprised users trying to understand why the following simple usage does not work:
#include <iterator>
template <typename T>
struct MyIterator : std::iterator<std::random_access_iterator_tag, T> {
value_type data; // Error: value_type is not found by name lookup
// ... implementations details elided ...
};
The reason of clarity alone was sufficient to persuade the LWG to update the standard library specification to no longer mandate the standard iterator adapators as deriving from std::iterator, so there is no further use of this template within the standard itself. Therefore, it looks like a strong candidate for deprecation.
You can also see STL's reasoning in LWG 2438. (h/t T.C.)
As for some other way of doing it, not really. You could basically implement your own version of std::iterator (which isn't too hard) or manually write out all of those typedefs (which isn't too hard either, and I actually prefer it for clarity).

As Barry states, the working group has decided that explicitly declaring the types in the class is more readable and leads to less surprises than inheriting from std::iterator.
It's not too hard, though, to convert to the explicit types (below example taken from www.fluentcpp.com here). Given a class that was declared like so:
class MyIterator
: public std::iterator<std::forward_iterator_tag, int, int, int*, int&>
{
...
The class without std::iterator becomes:
class MyIterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
// ...

Related

Why alias a template parameter in C++ standard containers?

Reviewing the code of Microsoft's STL (specifically std::vector), I came across the following lines of code (irrelevant code replaced with /* ... */):
// CLASS TEMPLATE vector
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector // varying size array of values
{
/* ... */
public:
/* ... */
using value_type = _Ty;
using allocator_type = _Alloc;
using pointer = typename _Alty_traits::pointer;
using const_pointer = typename _Alty_traits::const_pointer;
using reference = _Ty&;
using const_reference = const _Ty&;
using size_type = typename _Alty_traits::size_type;
using difference_type = typename _Alty_traits::difference_type;
/* ... */
};
I was wondering why the convention of assigning a type alias to a template type is used here?
I was wondering why the convention of assigning a type alias to a template type is used here?
Suppose you have a template function that accept an STL container (std::vector, std::deque, std::set, std::multi_set, ...)
template <typename T>
void foo (T const & t)
{
// ...
}
and that you need the type of contained values.
You can, inside foo(), simply write
using needed_type = typename T::value_type;
and this works for std::vector, std::deque, std::set, std::multi_set, std::array, std::map, std::multi_map, etc.
I was wondering why the convention of assigning a type alias to a
template type is used here?
Because it is
the standard way of doing,
makes less error-prone code,
less typing,
more readable, and
all the above makes life easy!
For instance, let's consider the const_pointer, public template alias of std::vector
using const_pointer = typename _Alty_traits::const_pointer;
At some point, you want to know this type and use in a function, how could have been possible without the above alias?
Of course, you can write
#include <memory> // std::allocator_traits
using const_pointer = typename std::allocator_traits<typename std::vector</*type*/>::allocator_type>::const_pointer;
anywhere in your program. But that tends to more error-prone situations (e.g. missing some typenames and so on), and more typing.
Therefore, it makes sense to collect those kinds of types of a container and provide public-aliases-types.
The C++ standard requires std::vector to provide a double-handful of nested names, and those are the ones the question is asking about. That's why value_type, allocator_type, pointer, etc. are there. You can use those names in your code when you need the types that they refer to. It's not at all uncommon to see, for example, std::vector<int>::iterator in user code to refer to the type of the iterator that std::vector<int> provides.
As to why they're written the way they are, that's a broader consistency issue. Templates throughout the Dinkumware implementation of the C++ standard library (which is what Microsoft ships) use _Ty as the base name for general types. When there are two types you'll see _Ty1 and _Ty2. When there's an internal typedef for a name it will be _Myty (for "My" type). That consistency makes maintaining the code easier.
That convention actually used for providing "traits". Naming for those types follows some convention that allows write functions like distance in terms of those traits and make it working for many containers.
And I'm pretty sure that having those types for standard C++ library is part of specification and they had to adhere it.

Why do *_iterators still require typedef something void after removing std::iterator?

I use ostreambuf_iterator as the case below:
Before c++17,
template< class CharT, class Traits = std::char_traits<CharT> >
class ostreambuf_iterator : public std::iterator<std::output_iterator_tag,
void, void, void, void>
requires us to determinate every parameter type of std::iterator, so, for std::ostreambuf_iterator, void is not so bad.
As we know, std::iterator deprecated in c++17. So, iterators, should typedef their member types inside their own body, for example:
Member type Definition
---------------------------------------------
value_type void
difference_type void
pointer void
reference void
iterator_category std::output_iterator_tag
Question:
Why should these void types still be typedefed? IOW, I think
Member type Definition
---------------------------------------------
iterator_category std::output_iterator_tag
is enough. After all, the motivations for deprecating std::iterator is just to simplify the standard(library). So there should be some reasons that I cannot figure out.
After googling, I found a question about on stack overflow, and the answer said:
Defining it as a void, on the other hand, can prevent errors like:
typename Iter::value_type v = *it; //useless with an output iterator if it compiled
But I think if we don't typedef value_type, such errors can also be prevented.
Because ISO C++17 std::iteartor_traits needs all the 5 of these member types in general, even if it is already SFINAE-friendly since C++17. If there are insufficient nested types, the instance of std::iterator_traits will have no members desired. Currently, this is not changed in the current draft.
See also iterator_traits SFINAE friendliness for rationale of the original proposal.

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?
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.

Why does reverse_iterator doubly define its nested types?

It appears that the iterator adaptor reverse_iterator doubly defines most of its nested types. In particular, it inherits publicly from std::iterator which exposes iterator_category, value_type, difference_type, pointer and reference. Except for iterator_category and value_type, these are all explicitly typedef'ed again in the class definition.
24.5.1.1
Class template reverse_iterator
[reverse.iterator]
namespace std {
template <class Iterator>
class reverse_iterator : public
iterator<typename iterator_traits<Iterator>::iterator_category,
typename iterator_traits<Iterator>::value_type,
typename iterator_traits<Iterator>::difference_type,
typename iterator_traits<Iterator>::pointer,
typename iterator_traits<Iterator>::reference> {
public:
typedef Iterator iterator_type;
typedef typename iterator_traits<Iterator>::difference_type difference_type;
typedef typename iterator_traits<Iterator>::reference reference;
typedef typename iterator_traits<Iterator>::pointer pointer;
// ... rest of the class
};
Question: why the repetitive definition? Is this just for purposes of exposition, or is there more to it? And why not redefine iterator_category and value_type?
For a while now, they've been moving away from using std::iterator as a base class, and toward just specifying that each iterator must define the correct type names.
When they specify the base class in the standard, that constrains implementations to implement the class that way, even though the only real intent was to specify that the iterator needs to define some names. In particular, you could is_base_of to determine whether std::iterator is a base class of std::reverse_iterator. No, there's nothing polymorphic, so it's pretty silly and pointless to do so, but if you do so, the current standard says it must return true.
It looks (to me) like this is more or less an accidental halfway-point in the process of moving from (more or less accidentally) requiring the use of std::iterator as a base class, and simply specifying the names that must be defined in std::reverse_iterator (and various other iterators as well, of course).
For those who care, the history of this includes:
N3931
Issue 2438
There's also so related discussion papers about deprecating unary_function and binary_function:
N3145
N3198
These were provided for roughly the same reasons as std::iterator (i.e., just to provide some typedefs in a derived class) so the reasoning behind removing them is fairly pertinent to ceasing to use std::iterator as a base class.
This is more of a guess, but all those redundant typedefs declare types that are used within specification of the class body of reverse_iterator. For example (C++03 IS):
pointer operator->() const;
reference operator[](difference_type n) const;
Since iterator<..> is a dependent base class, it won't be searched for the names pointer and reference. So typedefing those names makes the remaining spec simpler:
typename iterator_traits<Iterator>::pointer operator->() const;
typename iterator_traits<Iterator>::reference operator[](typename iterator_traits<Iterator>::difference_type n) const;
On the other hand, value_type does not appear within the class body, therefore it doesn't need a redundant typedef.

Why are type_traits implemented with specialized template structs instead of constexpr?

Is there any reason why the standard specifies them as template structs instead of simple boolean constexpr?
In an additional question that will probably be answered in a good answer to the main question, how would one do enable_if stuff with the non-struct versions?
One reason is that constexpr functions can't provide a nested type member, which is useful in some meta-programming situations.
To make it clear, I'm not talking only of transformation traits (like make_unsigned) that produce types and obviously can't be made constexpr functions. All type traits provide such a nested type member, even unary type traits and binary type traits. For example is_void<int>::type is false_type.
Of course, this could be worked around with std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>, but it wouldn't be as practical.
In any case, if you really want function-like syntax, that is already possible. You can just use the traits constructor and take advantage of the constexpr implicit conversion in integral_constant:
static_assert(std::is_void<void>(), "void is void; who would have thunk?");
For transformation traits you can use a template alias to obtain something close to that syntax:
template <bool Condition, typename T = void>
using enable_if = typename std::enable_if<Condition, T>::type;
// usage:
// template <typename T> enable_if<is_void<T>(), int> f();
//
// make_unsigned<T> x;
Note: this ends up looking more like a rant than a proper answer... I did got some itch reading the previous answers though, so please excuse me ;)
First, class traits are historically done with template structures because they predate constexpr and decltype. Without those two, it was a bit more work to use functions, though the various library implementations of is_base_of had to use functions internally to get the inheritance right.
What are the advantages of using functions ?
inheritance just works.
syntax can be more natural (typename ::type looks stupid TM)
a good number of traits are now obsolete
Actually, inheritance is probably the main point against class traits. It's annoying as hell that you need to specialize all your derived classes to do like momma. Very annoying. With functions you just inherit the traits, and can specialize if you want to.
What are the disadvantages ?
packaging! A struct trait may embed several types/constants at once.
Of course, one could argue that this is actually annoying: specializing iterator_traits, you just so often gratuitously inherit from std::iterator_traits just to get the default. Different functions would provide this just naturally.
Could it work ?
Well, in a word where everything would be constexpr based, except from enable_if (but then, it's not a trait), you would be going:
template <typename T>
typename enable_if<std::is_integral(T()) and
std::is_signed(T())>::type
Note: I did not use std::declval here because it requires an unevaluated context (ie, sizeof or decltype mostly). So one additional requirement (not immediately visible) is that T is default constructible.
If you really want, there is a hack:
#define VALUE_OF(Type_) decltype(std::declval<T>())
template <typename T>
typename enable_if<std::is_integral(VALUE_OF(T)) and
std::is_signed(VALUE_OF(T))>::type
And what if I need a type, not a constant ?
decltype(common_type(std::declval<T>(), std::declval<U>()))
I don't see a problem either (and yes, here I use declval). But... passing types has nothing to do with constexpr; constexpr functions are useful when they return values that you are interested in. Functions that return complex types can be used, of course, but they are not constexpr and you don't use the value of the type.
And what if I need to chain trais and types ?
Ironically, this is where functions shine :)
// class version
template <typename Container>
struct iterator { typedef typename Container::iterator type; };
template <typename Container>
struct iterator<Container const> {
typedef typename Container::const_iterator type;
};
template <typename Container>
struct pointer_type {
typedef typename iterator<Container>::type::pointer_type type;
};
template <typename Container>
typename pointer_type<Container>::type front(Container& c);
// Here, have a cookie and a glass of milk for reading so far, good boy!
// Don't worry, the worse is behind you.
// function version
template <typename Container>
auto front(Container& c) -> decltype(*begin(c));
What! Cheater! There is no trait defined!
Hum... actually, that's the point. With decltype, a good number of traits have just become redundant.
DRY!
Inheritance just works!
Take a basic class hierarchy:
struct Base {};
struct Derived: Base {};
struct Rederived: Derived {};
And define a trait:
// class version
template <typename T>
struct some_trait: std::false_type {};
template <>
struct some_trait<Base>: std::true_type {};
template <>
struct some_trait<Derived>: some_trait<Base> {}; // to inherit behavior
template <>
struct some_trait<Rederived>: some_trait<Derived> {};
Note: it is intended that the trait for Derived does not state directly true or false but instead take the behavior from its ancestor. This way if the ancestor changes stance, the whole hierarchy follows automatically. Most of the times since the base functionality is provided by the ancestor, it makes sense to follow its trait. Even more so for type traits.
// function version
constexpr bool some_trait(...) { return false; }
constexpr bool some_trait(Base const&) { return true; }
Note: The use of ellipsis is intentional, this is the catch-all overload. A template function would be a better match than the other overloads (no conversion required), whereas the ellipsis is always the worst match guaranteeing it picks up only those for which no other overload is suitable.
I suppose it's unnecessary to precise how more concise the latter approach is ? Not only do you get rid of the template <> clutter, you also get inheritance for free.
Can enable_if be implemented so ?
I don't think so, unfortunately, but as I already said: this is not a trait. And the std version works nicely with constexpr because it uses a bool argument, not a type :)
So Why ?
Well, the only technical reason is that a good portion of the code already relies on a number of traits that was historically provided as types (std::numeric_limit) so consistency would dictate it.
Furthermore it makes migration from boost::is_* just so easier!
I do, personally, think it is unfortunate. But I am probably much more eager to review the existing code I wrote than the average corporation.
One reason is that the type_traits proposal is older than the constexpr proposal.
Another one is that you are allowed to add specializations for your own types, if needed.
Probably because boost already had a version of type_traits that was implemented with templates.
And we all know how much people on the standards committee copy boost.
I would say the mainreason is that type_traits was already part of tr1 and was therefore basically guaranteed to end up in the standard in more or less the same form, so it predates constexpr. Other possible reasons are:
Having the traits as types allows for overloading functions on the type of the trait
Many traits (like remove_pointer) define a type instead of a value, so they have to be expressed in this way. Having different interfaces for traits defining values and traits defining types seems unnessecary
templated structs can be partial specialized, while functions can't, so that might make the implementation of some traits easier
For your second question: As enable_if defines a type (or not, if it is passed false) a nested typedef inside a struct is really the way to go