Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ template parameter with map declaration

What is wrong with this code:

Header:

#include <map>

using namespace std;

template<class T>
class ValueCollection
{
public:
    ValueCollection(void);

    int getValueCount(void);

    map<string, T> Values;
};

Implementation:

#include "ValueCollection.h"

ValueCollection<class T>::ValueCollection(void)
{
}

int ValueCollection<class T>::getValueCount(void)
{
    return Values.size();
}

Test:

#include "ValueCollection.h"

TEST(ValueCollection_TestCases, Default_Constructor_MapIsEmpty)
{
    ValueCollection<int>* target = new ValueCollection<int>;

    int expected = 0;
    int actual = target->getValueCount();

    ASSERT_EQ(expected, actual);
}

This is the error:

Error   1   error C2079: 'std::_Pair_base<_Ty1,_Ty2>::second' uses undefined class 'T'  c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility  167 1   Refactor01
like image 217
IUnknown Avatar asked Nov 25 '11 16:11

IUnknown


2 Answers

A couple of problems.

Your most immediate compiler error is caused by incorrect syntax in the implementation of the class template member functions. You must preface definition of a class template member with the template keyword. To wit:

template<class T> ValueCollection<T>::ValueCollection(void)
{
}

template<class T> int ValueCollection<T>::getValueCount(void)
{
    return Values.size();
}

There's another problem too, which will only become apparent later as your program grows. You can't define template functions or classes in one Translation Unit and use them in antoher. The compiler must have the complete definition available in each translation unit.

Typically, the way this is accomplished is by defining template functions directly in the header file, right where they are declared. In your case:

template<class T>
class ValueCollection
{
public:
    ValueCollection(void)
    {
    }

    int getValueCount(void)
    {
        return Values.size();
    }

    map<string, T> Values;
};

That's just one way to accomplish this -- there are others. Another way is to use the so-called "inclusion method":

ValueCollection.h

template<class T>
class ValueCollection
{
public:
    ValueCollection(void);

    int getValueCount(void);

    map<string, T> Values;
};

#include "ValueCollection.hpp:

ValueCollection.hpp

template<class T> ValueCollection<T>::ValueCollection(void)
{
}

template<class T> int ValueCollection<class T>::getValueCount(void)
{
    return Values.size();
}

Yet another method would be to supply a new definition for each translation unit that wants to use the templates, but this would be highly unusual. (In fact, I have never done this in my 15 years of C++ programming)

like image 61
John Dibling Avatar answered Sep 29 '22 13:09

John Dibling


implementation should read:

#include "ValueCollection.h"

template <class T>
ValueCollection<T>::ValueCollection(void)
{
}

template <class T>
int ValueCollection<T>::getValueCount(void)
{
    return Values.size();
}
like image 23
sehe Avatar answered Sep 29 '22 11:09

sehe