0

I am new to C++ so this is likely a simple mistake but this code is giving me problems for hours now. I am really just not sure what to try next.

EratosthenesHashMap.h

#pragma once

#include <unordered_map>
#include <boost/functional/hash.hpp>
#include "SieveOfEratosthenes.h"

template<class T>
class EratosthenesHashMap
{

public:
    EratosthenesHashMap(SieveOfEratosthenes& sieve);
    ~EratosthenesHashMap();

    unsigned int addValue(T& value);
    unsigned int getPrime(T& value) const;

private:
    SieveOfEratosthenes *sieve;
    std::unordered_map<T, unsigned int, boost::hash<T>> valueMap;
};

EratosthenesHashMap.cpp

#include "EratosthenesHashMap.h"

EratosthenesHashMap<class T>::EratosthenesHashMap(SieveOfEratosthenes& sieve) 
{
    this->sieve = &sieve;
};

unsigned int EratosthenesHashMap<T>::addValue(T& value)
{
    return 0;
}

unsigned int EratosthenesHashMap<T>::getPrime(T& value) const
{
    return 0;
}

Error:

EratosthenesHashMap.cpp
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\utility(331,10): error C2079: 'std::pair<const T,unsigned int>::first' uses undefined class 'T'
1>        with
1>        [
1>            T=T
1>        ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\xhash(305): message : see reference to class template instantiation 'std::pair<const T,unsigned int>' being compiled
1>        with
1>        [
1>            T=T
1>        ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\xhash(304): message : while compiling class template member function 'void std::_Hash_vec<std::allocator<std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<_Ty>>>>>::_Tidy(void) noexcept'
1>        with
1>        [
1>            _Ty=std::pair<const T,unsigned int>
1>        ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\xhash(313): message : see reference to function template instantiation 'void std::_Hash_vec<std::allocator<std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<_Ty>>>>>::_Tidy(void) noexcept' being compiled
1>        with
1>        [
1>            _Ty=std::pair<const T,unsigned int>
1>        ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\xhash(1933): message : see reference to class template instantiation 'std::_Hash_vec<std::allocator<std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<_Ty>>>>>' being compiled
1>        with
1>        [
1>            _Ty=std::pair<const T,unsigned int>
1>        ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\unordered_map(69): message : see reference to class template instantiation 'std::_Hash<std::_Umap_traits<_Kty,_Ty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>>' being compiled
1>        with
1>        [
1>            _Kty=T,
1>            _Ty=unsigned int,
1>            _Hasher=boost::hash<T>,
1>            _Keyeq=std::equal_to<T>,
1>            _Alloc=std::allocator<std::pair<const T,unsigned int>>
1>        ]
1>C:\Users\jpsie\source\repos\EratosthenesContainer\EratosthenesContainer\EratosthenesHashMap.h(20): message : see reference to class template instantiation 'std::unordered_map<T,unsigned int,boost::hash<T>,std::equal_to<T>,std::allocator<std::pair<const T,unsigned int>>>' being compiled
1>        with
1>        [
1>            T=T
1>        ]

I am trying to create a hashmap as a member variable with key type T, value type unsigned int, and I am using the boost library for a hash function.

  • 1
    1. You forgot `template` prior each method definition. 2 [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). – 273K Dec 30 '21 at 19:12
  • The error messages from MSVC are really unhelpful here. It seems to assume that you are trying to explicitly specialize, but the syntax for that would also be different. It should just fail at the attempted definition of `EratosthenesHashMap::EratosthenesHashMap` with a syntax error. – user17732522 Dec 30 '21 at 19:19
  • @user17732522, Since `class T` is a legitimate way to declare a class inline, the compiler's probably taking `EratosthenesHashMap`, instantiating the template precisely _because_ this syntax can't be a specialization or anything, and therefore `std::unordered_map`, which uses `std::pair`, and then finally seeing that a pair containing an incomplete class as a member won't work. The error itself makes a lot of sense to me, it's just a few layers deep. It could even be that the compiler sees `EratosthenesHashMap` and falls back to "this must start a declaration with type ...". – chris Dec 30 '21 at 19:45
  • @chris That could be an explanation, but MSVC just accepts explicit specialization without the `template<>` introduction. If you change `class T` to `T` you get the same error and with `int` instead of `T` it happily compiles. – user17732522 Dec 30 '21 at 19:50
  • @user17732522, Interesting. In that case, I rescind what I said. It seems like a bit of a happy coincidence that it makes sense with that logic. – chris Dec 30 '21 at 20:34

3 Answers3

1

The way to define a member of a template class is

template<class T>
EratosthenesHashMap<T>::EratosthenesHashMap(SieveOfEratosthenes& sieve) 
{
    this->sieve = &sieve;
};

On top of that, you should probably defined the templates in the header file, because otherwise they will only be usable in the same cpp file.

See Why can templates only be implemented in the header file?

BoP
  • 2,310
  • 1
  • 15
  • 24
1

It is rather difficult to have a template class split between header file and .cpp file and be easy to consume by callers. Instead, inline your entire template class in EratosthenesHashMap.h:

template<class T>
class EratosthenesHashMap
{

public:
    EratosthenesHashMap(SieveOfEratosthenes& sieve)
    {
        this->sieve = &sieve;
    }
    ~EratosthenesHashMap()
    {
    }

    unsigned int addValue(T& value)
    {
        return 0;
    }

    unsigned int getPrime(T& value) const
    {
        return 0;
    }

private:
    SieveOfEratosthenes* sieve;
    std::unordered_map<T, unsigned int, boost::hash<T>> valueMap;
};
selbie
  • 100,020
  • 15
  • 103
  • 173
1

The reason your code doesn't compile is that you can't break a class template into a .h file and .cpp file in the typical way.

Say you have a main.cpp file that uses EratosthenesHashMap<int> and you have EratosthenesHashMap broken into a .h and a .cpp as in your question, then main.cpp gets compiled completely independently of EratosthenesHashMap.cpp and needs to be able to link to an implementation of EratosthenesHashMap<int> but EratosthenesHashMap.cpp does not know anything about what types it will be applied to so this is impossible.

EratosthenesHashMap.cpp does not define a class; it defines a template; it can't be compiled into an object file that can be linked against.

Typically you use templates by providing a full implementation in a header for this reason.

jwezorek
  • 8,592
  • 1
  • 29
  • 46