【问题标题】:Unresolved external symbol with template function具有模板功能的未解析外部符号
【发布时间】:2014-06-13 04:11:04
【问题描述】:

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

错误 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++ function templates external linker-errors


【解决方案1】:

您不需要将源代码移动到头文件中。有一条捷径可能不是最漂亮的,但它确实有效。 您可以在 AssetManager.cpp 中添加一个基本上调用它的函数 GetAsset (...)。然后函数就解决了。它是一种模板专业化。使那个“特殊”调用的参数不做任何事情。

记住在 cpp 文件中包含 Mesh.h 而不是 h 文件,否则你会得到嵌套的包含,这会导致混乱。

问候 马蒂亚斯

【讨论】:

    【解决方案2】:

    C++ 不允许您在头文件中声明模板并在.cpp 文件中定义它。原因是模板只有在模板参数已知的情况下才能创建,无法提前编译。

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

    【讨论】:

    • 您确实可以单独声明和定义函数模板。你只需要做对。
    • 正确的分手方式是什么?顺便说一句,分离有效,即使我在 0.o 之前尝试过
    • @gha.st 是的,但这违背了模板的目的
    • @Caesar 你如何定义两个相互递归的函数模板而不事先声明一个?
    • @gha.st 你赢了这一轮。已编辑
    【解决方案3】:

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

    模板只是一种“宏”。当您在 SceneMagager.cpp 文件中使用方法 GetAsset&lt;Mesh*&gt; 时,C++ 编译器在该编译单元中搜索 GetAsset() 的源代码,以便将 T 类型名替换为 Mesh 并编译新方法(使用该替代方法即时创建)。但是SceneManager.cpp只知道AssetManager.h(不是实现GetAsset&lt;T&gt;的.cpp)所以没有真正的代码,编译失败。

    只需将您的 AssetManager::GetAsset 实现从 .cpp 文件移动到 .h 即可。

    【讨论】:

    • 函数模板必须在头文件中定义没有技术原因。事实上,C++ 甚至不知道“头文件”的概念。
    • 可以在 .cpp 文件中创建内联模板类,但是由于模板是一个定义,并且定义通常放在头文件中,因此将它们放在那里是有意义的
    【解决方案4】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-12
      • 1970-01-01
      • 2012-03-11
      • 2013-12-18
      • 2011-10-21
      • 2015-11-17
      • 1970-01-01
      相关资源
      最近更新 更多