【问题标题】:Visual studio won't compile template class with *.inl implementationVisual Studio 不会编译带有 *.inl 实现的模板类
【发布时间】:2013-07-26 10:30:09
【问题描述】:

我正在关注一本关于 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 文件中遇到了不寻常的错误,我没有编写它们,而是截取了屏幕截图:

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

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

我在纹理命名空间中有枚举(这就是“enumFile.h”)

namespace Textures
{
    enum ID {Landscape, Airplane, Missile};
}

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

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

【问题讨论】:

  • 您编写的代码使用 msvc 2012 编译。我刚刚选择了“enumFile.h”。所以问题出在其他地方
  • 我怀疑template &lt;typename Resource, typename Identifier&gt; void ResourceHolder&lt;Resource, Identifier&gt;::load(Identifier id, const std::string&amp; filename) 标识符必须是类或结构。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    这个问题是由 inl 文件的“文件类型”属性引起的。 Visual Studio 使用此属性来确定在构建项目时如何处理文件。例如,'C++ Header File'(.h/hpp) 不会自己编译(头文件中的代码块异常),但 'C/C++ Code'(.c/cpp)文件是可以的。

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这绝对有帮助!
      【解决方案4】:

      以上代码在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, "" );
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-15
        相关资源
        最近更新 更多