簡體   English   中英

未解析的外部符號與模板功能

[英]Unresolved external symbol with template function

我在網上搜索但我還沒有找到答案,為什么我收到這個錯誤:

錯誤1錯誤LNK2019:未解析的外部符號“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)在函數“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

這是我的代碼:

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()
{

}

在此先感謝您的回復!

C ++不允許您在頭文件中聲明模板並在.cpp文件中定義它。 原因是模板只能在模板參數已知時創建,因此無法提前編譯。

要解決您的問題,您需要在AssetManager.h文件中聲明和定義template <class T> T GetAsset(string assetName)

模板方法必須在頭文件中實現,而不是在CPP中實現。

模板只是一種“宏”。 當您在SceneMagager.cpp文件中使用方法GetAsset<Mesh*>時,C ++編譯器在該編譯單元中搜索GetAsset()的源代碼,以便使用Mesh替換T typename並編譯新方法(在與那個替代品一起飛行)。 SceneManager.cpp只知道AssetManager.h (而不是實現GetAsset<T>的.cpp),因此實際代碼不可用,編譯失敗。

只需將.cpp文件中的AssetManager::GetAsset實現移動到.h即可。

正如您的錯誤消息所示,將目標文件鏈接在一起失敗,因為函數AssetManager::GetAsset<Mesh*>不可用。

考慮以下簡單的難題:
編譯SceneManager.cpp ,編譯器會看到使用了AssetManager::GetAsset<Mesh*> ,因此在目標文件中添加了對它的引用。 但是,由於定義不可用,因此無法實際實例化功能模板。
編譯AssetManager.cpp ,編譯器會看到函數模板的定義,但沒有理由為任何T實例化它。

為了解決這個問題,只是得到的聲明時,他們立即定義模板的習慣-在你的情況下,定義移動AssetManager::GetAsset其在聲明AssetManager.h

您不需要將源移動到頭文件中。 有一條捷徑可能不是最美麗但它的確有效。 您可以在AssetManager.cpp中添加一個函數,該函數基本上使用GetAsset <Mesh *>(...)調用它。 然后功能解決了。 它是一種模板專業化。 使“特殊”調用的in-paramaters不做任何事情。

並且記得在cpp文件中包含Mesh.h而不是h文件,否則你會得到嵌套包含哪些內容。

關心馬蒂亞斯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM