我的代码库中有几个“资源”。除了一个类之外,它们都是类并共享相同的接口,ShaderProgram只有一种方式不同,它需要两个字符串来表示顶点和片段文件的文件名。
我有一个名为ResourceManager的模板类,它处理除着色器之外的所有这些资源,因为它需要两个文件而其他文件需要一个,我可以用模板特化来解决这个问题吗?它需要是ResourceManager看到GetOrLoadFromFile(字符串,字符串)和非(字符串)版本,而其他人则相反,他们看到(字符串)而不是(字符串,字符串)。 AttemptLoad也需要治疗。我如何为此制定解决方案请包括代码,我以前从未完成模板专业化。
template < class ResType > class ResourceManager
{
public:
ResourceManager(void);
~ResourceManager(void);
SmartPointer<ResType> GetOrLoadFromFile( const std::string & fileName );
//weak_ptr<ResType> GetResourceFromID( ResourceID & resID );
void DestroyResources();
void ReleaseResources();
void ReloadResources();
protected:
private:
SmartPointer<ResType> AttemptLoad( const std::string & fileName );
std::unordered_map<string, SmartPointer<ResType> > mResMap;
};
// Relevant methods ( SNIPPED )
template < class ResType> SmartPointer<ResType> ResourceManager<ResType>::GetOrLoadFromFile( const std::string & fileName )
{
if ( !mResMap.empty() )
{
auto index = mResMap.begin();
auto end = mResMap.end();
while ( index != end )
{
if ( index->first == fileName )
{
return index->second;
}
++index;
}
}
return AttemptLoad(fileName);
}
template < class ResType > SmartPointer<ResType> ResourceManager<ResType>::AttemptLoad( const std::string & fileName )
{
SmartPointer<ResType> pRes( new ResType() );
if ( pRes->LoadFromFile( fileName ) )
{
mResMap.insert( std::make_pair( fileName, pRes ) );
return pRes;
}
else
{
LogFailure("Failed to load resource file " + fileName)
return SmartPointer<ResType>(nullptr);
}
}
答案 0 :(得分:1)
如果两个班级都在你的控制范围内,我会提出一个不同的解决方案。为什么不将AttempLoad方法更改为
SmartPointer<ResType> AttemptLoad( const LoadConfiguration &p_loadConfiguration );
其中
class LoadConfiguration
{
public:
std::string FirstFileName;
};
和
class ExtendedLoadConfiguration : public LoadConfiguration
{
public:
std::string SecondFileName;
};
然后,您可以始终使用LoadConfiguration,并且每个AttemptLoad都可以获取他所需的内容。添加新参数将很容易,使用相同签名的代码更少,您不必使用模板专门化。
答案 1 :(得分:0)
只需实现'GetOrLoadFromFile'功能:
#include <string>
struct R1
{
void load (const std::string &name) {}
};
struct R2
{
void load (const std::string &name0, const std::string name1) {}
};
template<typename R>
struct M
{
R *get_or_load (const std::string &name)
{
R *p = new R();
p->load (name);
return p;
}
R *get_or_load (const std::string &name0,
const std::string &name1)
{
R *p = new R();
p->load (name0, name1);
return p;
}
};
M<R1> m1;
M<R2> m2;
int
main ()
{
R1 *p0 = m1.get_or_load ("foo");
// R1 *p1 = m2.get_or_load ("foo"); // error
R2 *q0 = m2.get_or_load ("foo", "bar");
// R2 *q1 = m1.get_or_load ("foo", "bar"); // error
}
“错误”的成员函数不会被实例化,除非实际调用它,在这种情况下编译器将退出诊断程序。
答案 2 :(得分:0)
模板背后的想法是在执行时间之前,即在编译时知道您的类型。如果这是真的,那么你要做的就是使用模板进行重载。所以,下面我只提出一个通用代码,你可以适应你的代码,在编译时进行重载。
请注意,为了避免两次编写代码,每个常用方法都放在基类中,只允许派生类出现分歧。
#include <memory>
#include <string>
#include <iostream>
using namespace std;
class Base
{
// put common codes here
};
template <typename ResType>
class ResourceManager : public Base
{
public:
unique_ptr<ResType> GetorLoad(const string &f) { cout << f << endl; return 0;}
};
// Specilizing class ResourceManager for string type
template <>
class ResourceManager<string> : public Base
{
public:
unique_ptr<string> GetorLoad(const string &f1, const string &f2) {cout << f1 << f2 << endl; return 0;}
};
int main()
{
ResourceManager<int> i;
ResourceManager<string> s;
i.GetorLoad("int");
s.GetorLoad("string", "string");
}
PS。要编译和测试这个例子,你需要使用gcc或clang ++编译器中的'--std = c ++ 11'标志