8

I'd like to ask help with the correct syntax to declare a std::map whose mapped_type is an inner class of a template class.

Please find in the code below a #if/#else block. The "#if 1" block has template class Outer that contains inner class Inner. The Outer defines function Func that takes a std map whose mapped_type is of type Inner.

#include <map>

#if 1
template<typename C, typename T>
class Outer
{
    public:
        Outer(const C& c, const T& t){}
        virtual ~Outer(){}

        class Inner
        {
            public:
                Inner(){}
                Inner(T t){}
                virtual ~Inner(){}

            protected:
                T mT;
        };

        void Func(std::map<C, Inner>& rMap);

    protected:
        std::map<C, Inner> mMap;
};

template<typename C, typename T>
void Outer<C, T>::Func(std::map<C, Outer::Inner>& rMap)
{
    std::map<C, Inner>::iterator iter;

    for (iter = rMap.begin(); iter != rMap.end(); ++iter)
    {
        mMap[iter->first] = iter->second;
    }
}

#else

class Outer
{
    public:
        Outer(const int& i, const double& d){}
        virtual ~Outer(){}

        class Inner
        {
            public:
                Inner() : mD(0){}
                Inner(const double d) : mD(d){}
                virtual ~Inner(){}

            protected:
                double mD;
        };

        void Func(std::map<int, Inner>& rMap);

    protected:
        std::map<int, Inner> mMap;
};

void Outer::Func(std::map<int, Inner>& rMap)
{
    std::map<int, Inner>::iterator iter;

    for (iter = rMap.begin(); iter != rMap.end(); ++iter)
    {
        mMap[iter->first] = iter->second;
    }
}

#endif

int main()
{
    return 0;
}

Compilation fails in Outer::Func(...) at the declaration of the std::map iterator, i.e. this line:

std::map<C, Inner>::iterator iter;

I've tried but cannot figure out what is wrong with the line of code.

For comparison/contrast, the "#else" block contains non-template code of similar nature. This code compiles.

The compile error and g++ version are:

>g++ main.cpp
main.cpp: In member function ‘void Outer<C, T>::Func(std::map<C, Outer<C, T>::Inner, std::less<_Key>, std::allocator<std::pair<const C, Outer<C, T>::Inner> > >&)’:
main.cpp:31: error: expected ‘;’ before ‘iter’
main.cpp:33: error: ‘iter’ was not declared in this scope

>g++ --version
g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11)
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Thank you for any help.

StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • `typename std::map::iterator iter;` – nshct Jul 30 '16 at 03:23
  • Possible duplicate of [Where and why do I have to put the "template" and "typename" keywords?](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) – Oktalist Jul 30 '16 at 14:07

1 Answers1

9

Since Inner is member of your template Owner<C, T>, it becomes a dependent name. That causes the identifier iterator, which in this case is a member of std::map<C, Inner>, to become a dependent name aswell.

This forces you to use the keyword typename, according to the rules:

typename std::map<C, Inner>::iterator iter;
~~~^~~~~

This is because the compiler can't be sure what certain constructs inside of your class mean since it does not know the exact types used for C and T yet:

Inside the definition of a template (both class template and function template), the meaning of some constructs may differ from one instantiation to another. In particular, types and expressions may depend on types of type template parameters and values of non-type template parameters.

The typename keyword is used to tell the compiler that the symbol you are accessing is indeed a type alias / type.

nshct
  • 1,197
  • 9
  • 29
  • 1
    I continue to be amazed at the font of wisdom that's out there. Thank you. – StoneThrow Jul 30 '16 at 04:37
  • To be honest, the explanation at the link you noted went above my head. If you're very familiar with this scenario, would you be able to explain it in simpler terms? When you say "The typename keyword is used to tell the compiler that the symbol you are accessing is indeed a type alias/type", what is "symbol" in this context? The inner class? Based on my parsing of the explanation, I'm unclear why this solution is required for this situation (inner classes), but is not required with "non-inner" classes. Appreciate if you have a deeper explanation, but no worries if not. Thank you. – StoneThrow Jul 30 '16 at 04:51