4

I have the following code:

template<typename T, typename Allocator = std::allocator<T> >
class Carray {
    // ...
    typedef T* pointer;
    typedef pointer iterator;
    // ...
};

Now I'm trying to do partial specialization for iterator_traits. It seems OK to me, but g++ 4.4.5 complains:

#include <iterator>

namespace std {
    template<typename T, typename Allocator>
    struct iterator_traits<typename Carray<T, Allocator>::iterator> { // line 128
        typedef T value_type;
        typedef typename Allocator::difference_type difference_type;
        typedef typename Allocator::reference reference;
        typedef typename Allocator::pointer pointer;
        typedef typename std::random_access_iterator_tag iterator_category;
    };
}

This is the full error message:

carray.h:128: error: template parameters not used in partial specialization:
carray.h:128: error:         ‘T’
carray.h:130: error: ‘Allocator’ has not been declared
carray.h:131: error: ‘Allocator’ has not been declared
carray.h:132: error: ‘Allocator’ has not been declared
orlp
  • 112,504
  • 36
  • 218
  • 315

1 Answers1

10

You shouldn't need a specialization at all here: iterator_traits is already specialized for pointer types and if you do end up with an iterator that is a class type, you can just define those required typedefs in the iterator class.

The problem is that in order to match the primary specialization, the compiler needs to take the arguments with which the template is used, plug them into the specialization, and see whether they match.

Consider what would happen in the following simplified scenario:

template <typename T> struct S { typedef int type; };

template <typename T> 
struct Traits { };

template <typename T> 
struct Traits<typename S<T>::type> { };

How is the compiler supposed to know what T to plug into S or whether some S<T>::type is really meant instead of just int?

The problem is that the nested typedef (::type) depends on the template parameter (T). When this is the case in a function argument list or in a partial specialization, the type T cannot be deduced (it is a "non-deduced context").

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Codepad is using a five year-old compiler that is probably quite buggy? I don't know; I don't have any compiler that old on my laptop. g++ 4.5.1 and Visual C++ 2010 SP1 both reject it outright; Clang 3.0r133044 emits a detailed warning that the partial specialization will never be used. – James McNellis Jun 18 '11 at 21:35
  • Codepad.org is a code paste website which can also compile your code for testing, which (according to http://codepad.org/about) uses g++ 4.1.2 with `-O -std=c++98 -pedantic-errors -Wfatal-errors -Werror -Wall -Wextra -Wno-missing-field-initializers -Wwrite-strings -Wno-deprecated -Wno-unused -Wno-non-virtual-dtor -Wno-variadic-macros -fmessage-length=0 -ftemplate-depth-128 -fno-merge-constants -fno-nonansi-builtins -fno-gnu-keywords -fno-elide-constructors -fstrict-aliasing -fstack-protector-all -Winvalid-pch` – orlp Jun 18 '11 at 21:37
  • So, the lesson is, don't trust old compilers or random websites that offer to compile your code for you. – James McNellis Jun 18 '11 at 21:38
  • Alright, I'll accept that as a legal reply :) I guess I'll just forget about specializing this iterator trait since it's defined already anyway. EDIT: Oh, I don't, normally, but I found it weird that this code didn't work with my compiler (4.5.5). In the question comments someone asked for the full reproducible code, which I pasted on codepad.org and I noticed it did compile there. – orlp Jun 18 '11 at 21:39