Unresolved external symbol with template function

12,273

Solution 1

C++ does not allow you to declare a template in a header file and define it in a .cpp file. The reason is that templates can only be created when the template parameters are known and so they can't be complied in advance.

To solve your problem, you will need to declare and define template <class T> T GetAsset(string assetName) in the AssetManager.h file

Solution 2

Template methods must be implemented in header files, not in CPP.

Templates are just a kind of "macro". When you use the method GetAsset<Mesh*> in your SceneMagager.cpp file, the C++ compiler searches in that compilation unit the source code for GetAsset() in order to substitute the T typename with Mesh and compile the new method (create on the fly with that substituion). But SceneManager.cpp only knows about AssetManager.h (not the .cpp where GetAsset<T> is implemented) so the real code is not available and the compilation fails.

Just move your AssetManager::GetAsset implementation from the .cpp file to the .h and it should work.

Solution 3

As your error message says, linking your object files together fails, because the function AssetManager::GetAsset<Mesh*> is not available.

Consider the following simple conundrum:
When compiling SceneManager.cpp, the compiler sees that AssetManager::GetAsset<Mesh*> is used and therefore adds a reference to it in the object file. However, since the definition is not available, it cannot actually instantiate the function template.
When compiling AssetManager.cpp, the compiler sees the definition of the function template, but no reason to instantiate it for any T at all.

To deal with this problem just get in the habit of immediately defining templates when declaring them - in your case, move the definition of AssetManager::GetAsset to its declaration in AssetManager.h.

Share:
12,273
Wouter Standaert
Author by

Wouter Standaert

Updated on September 16, 2022

Comments

  • Wouter Standaert
    Wouter Standaert over 1 year

    I searched around the web but I haven't found an answer yet, as to why I get this error:

    Error 1 error LNK2019: unresolved external symbol "public: class Mesh * __thiscall AssetManager::GetAsset(class std::basic_string,class std::allocator >)" (??$GetAsset@PAVMesh@@@AssetManager@@QAEPAVMesh@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: void __thiscall SceneManager::AddMesh(class std::basic_string,class std::allocator >)" (?AddMesh@SceneManager@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) C:\Users\Dirk\documents\visual studio 2010\Projects\OpenGameEngine\OpenGameEngine\SceneManager.obj

    Here is my code:

    AssetManager.h

    #pragma once
    #include <string>
    #include <map>
    #include "Asset.h"
    #include "Mesh.h"
    
    using namespace std;
    
    class AssetManager
    {
        public:
            AssetManager(string rootFolder);
            bool LoadAsset(string assetName, int assetType, string assetFile, bool subDirectory);
            void UnloadAsset(string assetName);
            template <class T> T GetAsset(string assetName);
            bool AddAssetSubDirectory(int assetType, string subDirectory);  
    
        private:
            string m_rootFolder;
            map<int, string> m_assetSubs;
            map<string, Asset*> m_assets;
    };
    

    AssetManager.cpp

    #include "AssetManager.h"
    
    AssetManager::AssetManager(string rootFolder)
    {
        m_rootFolder = rootFolder;
    }
    
    bool AssetManager::AddAssetSubDirectory(int assetType, string subDirectory)
    {
        if (m_assetSubs.find(assetType) == m_assetSubs.end())
        {
            m_assetSubs[assetType] = subDirectory;
            return true;
        }
        else
        {
            return false;
        }
    }
    
    bool AssetManager::LoadAsset(string assetName, int type, string assetFile, bool subDirectory)
    {
        string filePos;
        if (subDirectory)
        {
            filePos = m_rootFolder.append(m_assetSubs[type]).append(assetFile);
        }
        else
        {
            filePos = m_rootFolder.append(assetFile);
        }
        return true;
    }
    
    void AssetManager::UnloadAsset(string assetName)
    {
        if (m_assets.find(assetName) != m_assets.end())
        {
            m_assets.erase(assetName);
        }
    }
    
    template <class T> T AssetManager::GetAsset(string assetName)
    {
        if (m_assets.find(assetName) != m_assets.end())
        {
            return m_assets[assetName];
        }
        else
        {
            return null;
        }
    }
    

    SceneManager.h

    #pragma once
    #include <string>
    #include <map>
    #include "AssetManager.h"
    
    using namespace std;
    
    class SceneManager
    {
        public:
        static SceneManager* Instance();
        void AddMesh(string assetName);
        void RemoveMesh(string assetName);
        void Draw();
        void Run();
        void SetAssetManager(AssetManager*);
        void Destroy();
    
        private:
        SceneManager();
        SceneManager(SceneManager const&);
        ~SceneManager();
        SceneManager& operator=(SceneManager const&){};
        static SceneManager* m_Instance;
        AssetManager *m_assetMgr;
    
        private:
        map<string, Mesh*> m_staticMeshes;
    };
    

    SceneManager.cpp

    #include "SceneManager.h"
    #include "AssetManager.h"
    
    SceneManager* SceneManager::m_Instance = NULL;
    
    SceneManager::SceneManager()
    {
        m_assetMgr = 0;
    }
    
    SceneManager::SceneManager(SceneManager const&)
    {
    
    }
    
    SceneManager::~SceneManager()
    {
        delete m_assetMgr;
        m_assetMgr = 0;
    }
    
    void SceneManager::Destroy()
    {
        delete m_Instance;
        m_Instance = 0;
    }
    
    SceneManager* SceneManager::Instance()
    {
        if (!m_Instance)
            m_Instance = new SceneManager();
    
        return m_Instance;
    }
    
    void SceneManager::SetAssetManager(AssetManager *am)
    {
        m_assetMgr = am; 
    }
    
    void SceneManager::AddMesh(string assetName)
    {
        m_assetMgr->GetAsset<Mesh*>(assetName);
    }
    
    void SceneManager::RemoveMesh(string assetName)
    {
        if (m_staticMeshes.find(assetName) != m_staticMeshes.end())
        {
            m_staticMeshes.erase(assetName);
        }
    }
    
    void SceneManager::Draw()
    {
        for (map<string, Mesh*>::Iterator it = m_staticMeshes.begin(); it != m_staticMeshes.end(); ++it)
        {
            it->second->Draw();
        }
    }
    
    void SceneManager::Run()
    {
    
    }
    

    Thanks in advance for the responses!

  • danielschemmel
    danielschemmel almost 10 years
    You may indeed declare and define function templates separately. You just have to do it right.
  • Wouter Standaert
    Wouter Standaert almost 10 years
    And what would be the right way to sperate? Btw seperating worked, even though i tried before 0.o
  • Caesar
    Caesar almost 10 years
    @gha.st True, but then that defeats the purpose of a template
  • danielschemmel
    danielschemmel almost 10 years
    @Caesar How would you define two mutually recursive function templates without declaring one of beforehand?
  • danielschemmel
    danielschemmel almost 10 years
    There is no technical reason why function templates must be defined in header files. In fact, C++ does not even know the notion of "header files".
  • Caesar
    Caesar almost 10 years
    @gha.st You win this round. Edited
  • Constructor
    Constructor almost 10 years
    You can declare a template in a header file and define it in a .cpp file if you use an explicit template instantiation.
  • serup
    serup almost 7 years
    well inline template class could be made in the .cpp file, however since a template is a definitions and definitions generally goes in header files then it makes sense to put them there