【问题标题】:Using CRTP as an alternative to abstract static methods in C++11使用 CRTP 作为 C++11 中抽象静态方法的替代方案
【发布时间】:2016-04-25 20:27:59
【问题描述】:

我正在尝试实现一个通用资源管理器,它可以确保每个资源只用 C++11 加载一次。

我的第一次尝试:

resourcemanager.h

#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H

#include <map>
#include <memory>


template<typename T>
class ResourceManager {

public:
    static std::shared_ptr<T> load(std::string filePath);

private:
    static map<std::string, std::weak_ptr<T>> resources;
    virtual static std::shared_ptr<T> loadResource(std::string filePath) = 0;
};

#endif // RESOURCEMANAGER_H

#include "resourcemanager.h"

resourcemanager.cpp

using namespace std;

template<typename T>
map<string, weak_ptr<T>>  ResourceManager<T>::resources;

template<typename T>
shared_ptr<T> ResourceManager<T>::load(std::string filePath) {
    auto search = resources.find(filePath);
    if (search != resources.end()) {
        auto ptr = search->second.lock();
        if (ptr) {
            return ptr;
        }
    }
    auto ptr = loadResource(filePath);
    resources[filePath] = ptr;
    return ptr;
}

但是,由于抽象静态方法显然是被禁止的黑魔法,我尝试使用 CRTP:

resourcemanager.h

#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H

#include <map>
#include <memory>

template<typename T, class Derived>
class ResourceManager {

public:
    static std::shared_ptr<T> load(std::string filePath);

private:
    static std::map<std::string, std::weak_ptr<T>> resources;
    static std::shared_ptr<T> loadResource(std::string filePath);
};

#endif // RESOURCEMANAGER_H

resourcemanager.cpp

#include "resourcemanager.h"

using namespace std;

template<typename T, class Derived>
map<string, weak_ptr<T>>  ResourceManager<T, Derived>::resources;

template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::load(string filePath) {
    auto search = resources.find(filePath);
    if (search != resources.end()) {
        auto ptr = search->second.lock();
        if (ptr) {
            return ptr;
        }
    }
    auto ptr = ResourceManager::loadResource(filePath);
    resources[filePath] = ptr;
    return ptr;
}

template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::loadResource(string filePath) {
    return Derived::loadResource(filePath);
}

这看起来应该做我想做的事。但是,当我尝试使用它时,它在链接阶段失败了:

ma​​nagedstring.h

#ifndef MANAGEDSTRING_H
#define MANAGEDSTRING_H

#include "resourcemanager.h"

class ManagedString {

public:
    ManagedString(std::string filePath);
    std::string get();

private:
    std::shared_ptr<std::string> ptr;

    class StringManager : public ResourceManager<std::string, StringManager> {
    private:
        static std::shared_ptr<std::string> loadResource(std::string filePath);
    };
};

#endif // MANAGEDSTRING_H

ma​​nagedstring.cpp

#include "managedstring.h"

using namespace std;

ManagedString::ManagedString(string filePath) {
    ptr = StringManager::load(filePath);
}

string ManagedString::get() {
    return *ptr;
}

shared_ptr<string> ManagedString::StringManager::loadResource(string filePath) {
    // dummy implementation
    return make_shared<string>("foo");
}

ma​​in.cpp

#include <iostream>
#include "managedstring.h"

using namespace std;

int main() {
    ManagedString string1 = ManagedString("bar");
    ManagedString string2 = ManagedString("foobar");
    cout << string1.get() << endl; 
    cout << string2.get() << endl; 
}

当我尝试使用 g++ -std=c++11 -o bin -Wall main.cpp managedstring.cpp resourcemanager.cpp 编译它(使用 gcc 版本 5.3.0)时,我收到以下错误消息:

/tmp/ccgqljOQ.o: In function `ManagedString::ManagedString(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
managedstring.cpp:(.text+0xdd): undefined reference to `ResourceManager<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
 ManagedString::StringManager>::load(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'

这应该有效吗?这是编译器的缺点吗?还是我在尝试做一些我不应该做的事情。

我也想过改变我的设计,但我认为这还不错。请随意在这方面不同意我的观点。

【问题讨论】:

    标签: c++ c++11 crtp


    【解决方案1】:

    resourcemanager.h中,这一行:

    #include "resourcemanager.h"
    

    应该是:

    #include "resourcemanager.cpp"
    

    这似乎只对您的第一个示例有效,但同样适用于所有其他示例。
    否则,作为替代方案,将模板类的声明和定义放在同一个文件中。

    【讨论】:

    • 哇,我怀疑这可能很简单。但没想到会这么傻。谢谢!
    • @Faerbit 欢迎您,我什至还没有阅读完整的答案并尝试过。很好,很高兴能提供帮助。
    • 澄清一下:问题是我没有在头文件中声明我的函数。据我了解,使用模板时,所有函数都需要在头文件中声明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 2011-03-19
    • 2010-12-25
    • 2013-01-31
    • 1970-01-01
    相关资源
    最近更新 更多