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 astemplate<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
astemplate<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 namespacestd
unless otherwise specified. A program may add template specializations for any
standard library template to namespacestd
. 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
Objects of Different Classes in a Single Vector
Optimal Buffer Size for Write(2)
Why Do My Sfinae Expressions No Longer Work with Gcc 8.2
Reading from .Txt File into Two Dimensional Array in C++
Tcp Winsock: Accept Multiple Connections/Clients
In C++, Is There a Difference Between "Throw" and "Throw Ex"
How to Find How Much Memory Is Shared Between Forked Process with Copy-On-Write in Linux
How to Efficiently Wait for Cts or Dsr of Rs232 in Linux
Undefined Symbols in Crypto++/iOS 64-Bit Project
Partial Template Specialization Based on "Signed-Ness" of Integer Type
_Attribute_((Constructor)) Call Order Confusion
Best C++ Development Environment in Linux
Improving Mmap Memcpy File Read Performance
Linux Executable Can't Find Shared Library in Same Folder
How to Turn on Multi-Cpu/Core C++ Compiles in the Visual Studio Ide (2008)