Visual Studio不会使用* .inl实现编译模板类

时间:2013-07-26 08:26:50

标签: c++ visual-studio templates visual-c++ compiler-errors

我正在阅读一本关于SFML游戏开发的书,但我仍然坚持第二章,因为我无法编译我刚才编写的代码。

这几乎是书中的逐字复制(除了成员变量名和例外文本)。我有使用C ++和模板的经验,但我以前从未见过这个错误,现在我已经盯着这几个小时了,我没看到这个代码有什么问题。

这是我的* .h文件:

#pragma once
#include <map>
#include <memory>
#include <string>
#include <stdexcept>
#include <cassert>
#include "enumFile.h"

template <typename Resource, typename Identifier>
class ResourceHolder
{
public:
    ResourceHolder(void);
    ~ResourceHolder(void);

    void load(Identifier id, const std::string & filename);

    template <typename Parameter>
    void load(Identifier id, const std::string & filename, 
              const Parameter& secondParam);

    Resource & get(Identifier id);
    const Resource & get(Identifier id) const;

private:
    std::map<Identifier, std::unique_ptr<Resource>> resourceMap;
};

#include "ResourceHolder.inl"

这是我的* .inl文件:

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename)
{
    // Create and load resource
    std::unique_ptr<Resource> resource(new Resource());
    if (!resource->loadFromFile(filename))
        throw std::runtime_error("Failed to load resource: " + filename);

    // If loading successful, insert resource to map
    auto inserted = resourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);
}

template <typename Resource, typename Identifier>
template <typename Parameter>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename, 
                                                const Parameter& secondParam)
{
    // Create and load resource
    std::unique_ptr<Resource> resource(new Resource());
    if (!resource->loadFromFile(filename, secondParam))
        throw std::runtime_error("Failed to load resource: " + filename);

    // If loading successful, insert resource to map
    auto inserted = resourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);
}

template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
    auto found = resourceMap.find(id);
    assert(found != resourceMap.end());

    return *found->second;
}

template <typename Resource, typename Identifier>
const Resource& ResourceHolder<Resource, Identifier>::get(Identifier id) const
{
    auto found = resourceMap.find(id);
    assert(found != resourceMap.end());

    return *found->second;
}

很抱歉很多代码,但我绝望了。我在* .inl文件中得到了不寻常的错误,而不是写它们,我拍了一个截图: VS2012 errors

知道如何解决这个问题吗?

编辑:关于如何使用班级的一句话。

我有一个纹理命名空间(就是“enumFile.h”是

)里面的枚举
namespace Textures
{
    enum ID {Landscape, Airplane, Missile};
}

当我想使用该类时,我按如下方式使用它:

ResourceHolder<sf::Texture, Textures::ID> textureHolder;
textureHolder.load(Textures::Airplane, "path/to/texture.png");

4 个答案:

答案 0 :(得分:3)

从VS中的项目中删除或排除ResourceHolder.inl文件,然后再次添加它解决了我的问题。我不知道为什么会这样,但现在编译得很好。

答案 1 :(得分:1)

此问题是由inl文件的“文件类型”属性引起的。 Visual Studio使用此属性来确定在构建项目时如何处理文件。例如,“C ++头文件”(.h / hpp)本身不会编译(头文件中的代码块是例外),但是“C / C ++代码”(.c / cpp)文件是。

如果您首先添加了C / CPP文件并将其文件扩展名更改为inl,则inl文件的“文件类型”必须在VC项目中保留为“C / C ++代码”。但是inl文件不是没有模板参数的可编译代码。所以它的文件类型不应该是'C / C ++ Code'。而是将其设置为“C ++头文件”或“文档”。

当您将文件添加到VC ++项目中时,VS会根据其扩展名自动设置“文件类型”属性。 VS为inl文件设置'Document'文件类型。这就是删除和重新添加inl文件解决问题的原因。

请注意,编译模板化代码实际上是在编译器处理一些使用它的代码时执行的,例如头文件。例如,如果在静态库项目中实现了模板类,并且未在库项目中使用该模板类,则在构建该lib项目时将不会编译该类。相反,当您构建一个使用lib文件的项目和带有模板参数的模板类时,它将被编译。这就是你应该在头文件中实现模板类或模板化函数的成员函数的原因,或者像头文件中包含的inl一样。

答案 2 :(得分:1)

我知道这是一个古老的话题,但我刚才遇到了同样的问题(顺便说一下,我正在阅读相同的SFML书),而我刚刚找到了一个快速而简单的解决方案,所以我希望分享它以供将来参考。

在Visual Studio的“解决方案资源管理器”选项卡中,找到您的.inl文件并右键单击。然后,选择“从项目中排除”选项。完成!

答案 3 :(得分:0)

上面的代码在msvc2012上编译。我刚删除了#include "enumFile.h并添加了ResourceHolder的ctor和dctor。这是我的main

更新

#include "resourceholder.h"

struct Resource
{
    bool loadFromFile( const std::string& filename )
    {
        return true;
    }
};

int main()
{
    Resource r;
    ResourceHolder< Resource, int > rh;
    rh.load( 30, "" );
}

这里的问题是标识符不能是枚举。标识符必须是结构或类。你可以得到一些东西

namespace Texture { struct a_tag{ int id }; struct b_tag{ int id }; }

或使用少量元编程:

/** Helper class to use integer in template parameters. */
template < Texture::ID >
struct EnumToStruct {
    static const Texture::ID value = e;
};

int main()
{
    Resource r;
    ResourceHolder< Resource, EnumToStruct<Texture::Car> > rh;
    rh.load( 30, "" );
}