未解析的外部符号与模板功能

时间:2014-04-27 18:14:00

标签: c++ function templates external linker-errors

我在网上搜索但我还没有找到答案,至于为什么我会收到这个错误:

  

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

}

提前感谢您的回复!

4 个答案:

答案 0 :(得分:12)

C ++不允许您在头文件中声明模板并在.cpp文件中定义它。原因是模板只能在模板参数已知时创建,因此无法提前完成。

要解决您的问题,您需要在template <class T> T GetAsset(string assetName)文件中声明和定义AssetManager.h

答案 1 :(得分:3)

模板方法必须在头文件中实现,而不是在CPP中实现。

模板只是一种&#34;宏&#34;。当您在GetAsset<Mesh*>文件中使用方法SceneMagager.cpp时,C ++编译器会在该编译单元中搜索 GetAsset()的源代码,以替换T类型名称使用Mesh并编译新方法(使用该替换创建)。但SceneManager.cpp只知道AssetManager.h(而不是实现GetAsset<T>的.cpp),因此实际代码不可用,编译失败。

只需将您的AssetManager::GetAsset实施从.cpp文件移至.h即可。

答案 2 :(得分:1)

正如您的错误消息所示,将目标文件链接在一起失败,因为函数AssetManager::GetAsset<Mesh*>不可用。

考虑以下简单的难题:
编译SceneManager.cpp时,编译器会看到使用了AssetManager::GetAsset<Mesh*>,因此在目标文件中添加了对它的引用。但是,由于定义不可用,因此无法实际实例化功能模板 编译AssetManager.cpp时,编译器会看到函数模板的定义,但没有理由为任何T实例化它。

要解决这个问题,请养成在声明模板时立即定义模板的习惯 - 在您的情况下,将AssetManager::GetAsset的定义移至AssetManager.h中的声明。

答案 3 :(得分:-1)

您不需要将源移动到头文件中。有一条捷径可能不是最美丽但它的确有效。 您可以在AssetManager.cpp中添加一个基本上用它自己调用的函数 GetAsset&lt;网格*&gt; (......)。然后功能解决了。它是一种模板专业化。使“特殊”调用的in-paramaters无效。

并且记得在cpp文件中包含Mesh.h而不是h文件,否则你会得到嵌套包含哪些内容会导致混乱。

此致 的Mattias