Visual Studio не будет компилировать класс шаблона с *.inl-реализацией

Я следую книге о разработке игр SFML, но я застрял во второй главе, потому что я не могу скомпилировать код, который я только что написал.

Это почти по одному слову копируется из книги (кроме имени переменной-члена и текста исключения). У меня есть опыт работы с С++ и шаблонами, но я никогда не видел эту ошибку раньше, и я смотрел на это несколько часов, и я не вижу ничего плохого в этом коде.

Вот мой *.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

Любая идея, как это исправить?

EDIT: слово о том, как используется класс.

У меня есть и перечисление внутри пространства имен текстур (вот что такое "enumFile.h" )

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

Когда я хочу использовать класс, я использую его следующим образом:

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

Ответ 1

Удаление или исключение файла ResourceHolder.inl из проекта в VS, а затем добавление его снова решило проблему для меня. Я понятия не имею, почему это сработало, но теперь оно прекрасно компилируется.

Ответ 2

Эта проблема вызвана свойством "Тип файла" вашего inl файла. Visual Studio использует это свойство, чтобы определить, как обрабатывать файл при построении проекта. Например, "Файл заголовка С++" (. H/hpp) не будет скомпилирован сам по себе (блок кода в файле заголовка является исключительным), но файлы "C/С++ Code" (.c/cpp) имеют значение.

Если вы добавили сначала файл C/CPP и изменили его расширение файла на inl, "Тип файла" inl файла должен оставаться в качестве "C/С++ Code" в проекте VC. Но inl файл не является компилируемым кодом без аргументов шаблона. Таким образом, тип файла не должен быть "C/С++ Code" . Вместо этого установите его в "Файл заголовка С++" или "Документ".

Когда вы добавляете файлы в проект VС++, VS автоматически устанавливает свойство "Тип файла" в соответствии с их расширением. И VS устанавливает тип файла "Документ" для inl файлов. Вот почему удаление и повторное добавление этого inl файла решает вашу проблему.

Обратите внимание, что компиляция шаблонного кода фактически выполняется, когда компилятор обрабатывает некоторые коды, которые его используют, например заголовочный файл. Например, если вы внедрили класс шаблона в проект статической библиотеки и не использовали его в проекте библиотеки, этот класс не будет скомпилирован при создании этого проекта lib. Вместо этого, когда вы создаете проект, который использует файл lib и класс шаблона с аргументами шаблона, он будет скомпилирован. Вот почему вы должны реализовывать функции-члены класса шаблона или шаблонные функции в файле заголовка или что-то еще, например, inl, которое включено в файл заголовка.

Ответ 3

Я знаю, что это старая тема, но у меня была такая же проблема, пока несколько минут назад (я, кстати, читаю ту же книгу SFML), и я только что нашел быстрое и простое решение, поэтому я хочу поделиться им для дальнейшего использования.

На вкладке "Проводник решений" в Visual Studio найдите файл .inl и RIGHT CLICK. Затем выберите опцию "Исключить из проекта" . Готово!

Ответ 4

Вышеприведенный код компилируется на msvc2012. Я просто удалил #include "enumFile.h и добавил ctor и dctor ResourceHolder. это мой 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, "" );
}