2

I am having trouble using a private std::map variable from within a class template. Can anyone explain why the following (simplified example) does not work, and what I should be doing instead?

#include <iostream>
#include <map>

template <class T>
class Container
{
    private:
        typedef std::map<long, T> Map;
        typedef Map::iterator Iterator; // <-- this is line 10
        Map items;
    public:
        void insert(const long id, const T& item) { items[id] = item; }
        void print()
        {
            for (Iterator iter = items.begin(); iter != items.end(); iter++)
            { std::cout << iter->second << std::endl; }
        }
};

int main()
{
    Container<int> container;

    container.insert(300, 1);
    container.insert(200, 2);
    container.insert(100, 3);

    container.print();

    return 0;
}

The resulting error is somewhat cryptic:

t.cpp:10: error: type 'std::map, std::allocator > >' is not derived from type 'Container'

t.cpp:10: error: ISO C++ forbids declaration of 'iterator' with no type

t.cpp:10: error: expected ';' before "Iterator"

e.James
  • 116,942
  • 41
  • 177
  • 214
  • For the record, I am using MinGW g++ 3.4.5 (mingw-vista special r3) in a MSYS console on Windows XP. – e.James Nov 30 '11 at 21:46
  • possible duplicate of the duplicates listed in [map iterator in template function unrecognized by compiler](http://stackoverflow.com/questions/3184682/map-iterator-in-template-function-unrecognized-by-compiler) – CB Bailey Nov 30 '11 at 21:50

1 Answers1

8

You need to qualify the dependant typename:

    typedef typename Map::iterator Iterator;

Rationale: the compiler, at the time of parsing the template, cannot work out whether is a type or a value expression. This is because the names depend on the template argument(s), which are only known at instantiation time.

You have to help the compiler out.


Sidenotes:

  • MSVC seems to be lax with this rule, as it's template instantiation engine behaves in non-standard ways, and the name lookups aren't done until instantiation time anyway

  • sometime, dependent member templates need to be qualified as well:

    template <typename aTuple> struct ContrivedSample
    {
        aTuple data;
    
        void foo()
        {
           auto v = data.template get<0>(); // template required
        }
    };
    
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    I just completed an explanation – sehe Nov 30 '11 at 21:51
  • In that context it tells the compiler to expect a type, instead of the default, a value expression. – Mordachai Nov 30 '11 at 21:52
  • Suggestion! If your compiler supports C++0x, try using the `auto` keyword: `auto iter = items.begin();` – Anthony Nov 30 '11 at 21:52
  • So, if I understand correctly: the compiler will interpret `typename Map::iterator` as a *type* expression, but not `Map::iterator`, because it has no way to determine what `::iterator` refers to without knowing details about `Map`. Is that the gist of it? – e.James Nov 30 '11 at 21:54