Specializing Iterator_Traits

specializing iterator_traits

Yes. The compiler cannot deduce T from Container<T>::iterator because it is non-deducible context, which in other words means, given Container<T>::iterator, the value of T cannot uniquely and reliably be deduced (see this for detail explanation).

The only solution to this problem is that you've to fully specialize iterator_traits for each possible value of iterator which you intend to use in your program. There is no generic solution, as you're not allowed to edit the Container<T> class template.

How to avoid specializing iterator_traits for each possible instantiation of a templated iterator?

std::iterator_traits<Iterator>::something is simply Iterator::something by default. Thus, simply add the typedefs into your reversed_iterator type:

struct reversed_iterator {
using difference_type = typename std::iterator_traits<IT>::difference_type;
using value_type = typename std::iterator_traits<IT>::value_type;
using pointer = typename std::iterator_traits<IT>::pointer;
using reference = typename std::iterator_traits<IT>::reference;
using iterator_category = /* appropriate category */;

// ...
};

What is the design purpose of iterator_traits?

It seemed that in general, the iterator_traits just extracts properties that defined explicitly in iterator. If a iterator_traits is to be used, one can use the corresponding iterator directly.

Not all iterators can type aliases as members.

What's the benefit this indirection?

To allow all iterators to have a uniform interface. More specifically, pointers are iterators too and as non-class types, pointers cannot have members.

So, when for example the user of an iterator wants the answer to the question "What is the type of the object pointed by an_iterator_type", they cannot use an_iterator_type::value_type because that cannot be defined for all iterators - specifically pointers. What they can use is std::iterator_traits<an_iterator_type>::value_type which can be defined for all iterators - including pointers. That is the purpose of std::iterator_traits.


The standard defines the std::iterator_traits<T*> specialisations for pointers. Although you can, you don't need to define specialisations for custom iterator types because you can instead define the types as members, which will be used by the generic definition of std::iterator_traits.

The intention of std::iterator was to be optionally used as a base class to help define the member types for custom iterators. However its use was never necessary, it is considered to be a flawed design and its use has long been discouraged. Since C++17, it has been deprecated and it may be removed from future standards.

An example that hopefully demonstrates the problem of the design of std::iterator:

// using the deprecated std::iterator
struct iterator : std::iterator<
std::input_iterator_tag, // OK so far
long, // can you guess what member this argument defines?
long, // how about this?
const long*, // you can probably guess this one
long> // still, no clue
{
...

// recommended way without using std::iterator
struct iterator
{
using iterator_category = std::input_iterator_tag;
using value_type = long;
using difference_type = long;
using pointer = const long*;
using reference = long;
...

How to specialize a function template with iterator traits?

You can avoid specializations, or writing another overload. Instead, you can use conditional_t to select a specific type according to some condition

// alias for convenience
using T = typename std::iterator_traits<ForwIt>::value_type;

// if T is int, value_type is double, otherwise it's just T
using value_type = std::conditional_t<std::is_same_v<T, int>, double, T>;

For the return type, simply use auto, and the correct type will be deduced from the type of baz.

Here's a demo

Explicit specialization of std::iterator_traitschar * after instantiation (CLang)

The compiler is complaining that you're trying to specialize a template after instantiating the generic template -- by that point in time, the compiler has already used the generic template for the instantiation, and it can't go back and use your specialization instead. In other words, something like this:

template <typename T>
struct X
{
// Generic implementation
};

// Instantiate template by using it in any way
X<int> foo;

template<>
struct X<int>
{
// Specialization implementation for int
};

The fix is to define the specialization before it's instantiated, so in this example, you'd move the X<int> specialization to before where X<int> is used.

Note that the STL already defines specializations of std::iterator_trait for pointer types, so there's no need to define your own specialization here for char*. You'd typically only do that for user-defined iterator types which are not pointers. See §24.3.1/2 of the C++03 standard:

[The template iterator_traits<Iterator>] is specialized for pointers as

template<class T> struct iterator_traits<T*> {
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef random_access_iterator_tag iterator_category;
};

and for pointers to const as

template<class T> struct iterator_traits<const T*> {
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef const T* pointer;
typedef const T& reference;
typedef random_access_iterator_tag iterator_category;
};

So there's no point to providing your own std::iterator_traits<char*> specialization. Since char* is not a user-defined type, it's also undefined behavior according to the standard. §17.4.3.1/1 says:

It is undefined for a C + + program to add declarations or definitions to namespace std or namespaces
within namespace std unless otherwise specified. A program may add template specializations for any
standard library template to namespace std. Such a specialization (complete or partial) of a standard
library template results in undefined behavior unless the declaration depends on a user-defined name of
external linkage and unless the specialization meets the standard library requirements for the original template.163)

163) Any library code that instantiates other library templates must be prepared to work adequately with any user-supplied specialization
that meets the minimum requirements of the Standard

c++ failed to specialize function template 'iterator_traits'

You should pass iterators to select_randomly, not indexes. Here is the proper function call: std::vector<Move>::iterator it = select_randomly(possibleMoves.begin(), possibleMoves.end()); To learn more about iterators visit http://www.cprogramming.com/tutorial/stl/iterators.html

How can I specialize an algorithm for iterators that point to complex values?

You can use SFINAE and std::iterator_traits to constrain the "specialized" template. You also need a helper to check if the value_type returned by the iterator trait is a specializartion of std::complex. That code is

template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};

template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};

And was written by Quentin here

Using that you would have

template <typename InputIt, 
std::enable_if_t<!is_specialization<typename std::iterator_traits<InputIt>::value_type, std::complex>::value, bool> = true>
void DoSomething(InputIt first, InputIt last)
{
cout << "Regular Double" << endl;

for (; first != last; ++first)
{
cout << *first << endl;
}
}

template <typename InputItToComplex,
std::enable_if_t<is_specialization<typename std::iterator_traits<InputItToComplex>::value_type, std::complex>::value, bool> = true>
void DoSomething(InputItToComplex first, InputItToComplex last)
{
cout << "Complex Double" << endl;

for (; first != last; ++first)
{
cout << *first << endl;
}
}


Related Topics



Leave a reply



Submit