I have a template function, that needs to be specialized for iterators. So what I did was along the lines of:
template <typename T>
void function2(T whatever, typename std::iterator_traits<T>::pointer) // ... iterator
template <typename T>
void function2(T whatever, ...) // ... non-iterator
template <typename T>
void function(T whatever) {
function2(whatever, NULL);
}
And I’ve hit a wall, because Microsoft standard library specializes std::iterator_traits for all numeric types (bool, char, int, float…). And it does it so that reference and pointer are non-void, despite the fact that neither operator* nor operator-> can be called on those types.
Ok, I can checki the std::iterator_traits<T>::category derives std::input_iterator (actually I think std::forward_iterator is more appropriate in my case) at the cost of some more complex template machinery.
I would however be interested in knowing:
- Why do they define
iterator_traitsfor types, that don’t conform to the iterator concept (even output iterators need at least unaryoperator*, none of these types have one. - Are they violating C++ specification in doing so? Not that Microsoft wouldn’t be violating it all over the place, but if they are I would be satisfied with compiler-specific workaround, if they don’t obviously not.
- And is it even workable in general anyway? It appears the
std::iterator_traits<T>::pointeralways exists, but is undefined and that leads to error rather than SFINAE.
In order to use
iterator_traits<T>,Tmust be an iterator. If it isn’t, the behaviour is undefined. You cannot detect whether a typeTis an iterator at compile time. It is not even theoretically possible, since a type is allowed to support the same operators and typedefs as iterators, so that an implementation’s genericiterator_traits<T>can be instantiated without any error or warning message, but with a completely different meaning.Thinking about it, since your comment clarifies that a reasonable guess is good enough, I think you’re best off using SFINAE and
enable_ifto detect core operations (unary*and prefix++), usingstd::iterator_traits<T>::pointeronly if those conditions are met.