Here are the fragments of my linked_list template :
#include <iostream>
#include <iterator>
template < class >
struct linked_list {
struct iterator_base : public std::iterator< std::bidirectional_iterator_tag , int >
{
typename std::iterator_traits< iterator_base >::pointer operator -> () const {
std::cerr << __func__ ;
return nullptr ; } ;
};
using difference_type = typename std::iterator_traits< iterator_base >::difference_type ;
} ;
int main ()
{
int * inullptr = linked_list< int >::iterator_base().operator->() ;
return 0 ;
}
When I leave using...
line uncommented, the code doesn't compile.
g++5.4 :
list2.cxx:105:66: error: no type named ‘pointer’ in ‘struct std::iterator_traits<linked_list<int>::iterator_base<(linked_list<int, std::allocator<int> >::constantness)1u> >’
typename std::iterator_traits< iterator_base >::pointer operator -> () const { return &( to_obj_node( current_node_ ) -> object() ) ; }
icpc :
list.cxx(105): error: incomplete type is not allowed typename std::iterator_traits< iterator_base >::pointer operator -> () const { return &( to_obj_node( current_node_ ) -> object() ) ; }
Without that line all compiles fine.
the question is : What happens, when i'm commenting using difference_type = typename std::iterator_traits< iterator >::difference_type;
in above code ( only with such changes code compiles ).?
========================================================================== res.on.functions/2.5
- In particular, the effects are undefined in the following cases:...
- if an incomplete type ([basic.types]) is used as a template argument when instantiating a template component, unless specifically allowed for that component.
and historical discussion on that.
The error here is that the type std::iterator_traits< iterator_base >
is still incomplete and at the time that you want to access ::pointer
does not yet provide that pointer
member.
The class std::iterator_traits<iterator_base>
is being instantiated by typename std::iterator_traits< iterator_base >::difference_type
, because it's used on the left side of a ::
, and because it has not yet been instantiated. This triggers the instantiation of linked_list<int>::iterator_base
because the body of iterator_traits
uses that class to define its various member typedefs - for example the instantiation could happen at a line that looks like typedef typename Iterator::value_type value_type;
in the Standard library.
What follows is the use of std::iterator_traits< iterator_base >::pointer
in your nested class. This time, iterator_traits<iterator_base>
is already being instantiated, so nothing is done, and ::pointer
is searched. But since that wasn't declared yet, it can't found.
Note that when you comment-out the using
line, nothing in the code will instantiate the nested class body anymore (the body of members of class templates are "lazily instantiated"), so this can't be a measure for or against the validity of the constructs inside of that nested class body.