遍历类型列表



如何遍历类型列表而不创建每个类型的实例?

在我的示例中,我有一个父类,其方法getByName返回子类的实例。getByName方法完全失效了,因为您不能创建类型数组。实现这一目标的最佳方式是什么?

一个解决方案是创建一个name指针数组,但如果有多个变量(不仅仅是name),我想检查,这将变得混乱。

我基本上想要一个干净的解决方案,使用一个循环,而不是一系列的if语句。

#include <string>
struct Number {
    Number(const std::string &name) :name(name) {}
    // fix me!
    static Number* getByName(const std::string &name) {
        typedef types[] = {
            One,
            Two,
            Three,
        }
        for (int i = 0; i < 3; ++i) {
            if (name == types[i]::name)
                return new types[i]();
        }
        return nullptr;
    }
    const std::string name;
};
struct One :Number {
    One() :Number(name) {}
    const static std::string name;
};
struct Two :Number {
    Two() :Number(name) {}
    const static std::string name;
};
struct Three :Number {
    Three() :Number(name) {}
    const static std::string name;
};
const std::string One::name = "one";
const std::string Two::name = "two";
const std::string Three::name = "three";

你可以像下面这样实现你的工厂

template <typename T>
static std::pair<std::string, std::function<Number*()>> register_helper()
{
    return { T::name, []() { return new T{}; }};
}
static Number* getByName(const std::string &name) {
    static const std::map<std::string, std::function<Number*()>> factories = {
         register_helper<One>(),
         register_helper<Two>(),
         register_helper<Three>()
    };
    auto it = factories.find(name);
    if (it == factories.end()) {
        return nullptr;
    } else {
        return it->second();
    }
}

现场演示

不能以有意义的方式直接存储类型;但是,您可以存储指向工厂函数的指针,这应该同样有用:

#include <iostream>
#include <string>
#include <map>
using namespace std;
struct Number;
Number* oneCreator();
Number* twoCreator();
Number* threeCreator();
struct Number {
    typedef Number* (*creatorFP)();
    typedef map<string, creatorFP> CreatorMap;
    static Number* getByName(const string &name) {
        // maybe initialise this map somewhere else
        CreatorMap creators;
        creators.insert(make_pair(string("one"), &oneCreator));
        creators.insert(make_pair(string("two"), &twoCreator));
        creators.insert(make_pair(string("three"), &threeCreator));
        CreatorMap::iterator creator = creators.find(name);
        if (creator != creators.end()) {
            return (*(creator->second))();
        }
        return NULL;
    }
    virtual void f() { cout << "NUMBER" << endl; }
};
struct One : Number {
    virtual void f() { cout << "ONE" << endl; }
};
struct Two : Number {
    virtual void f() { cout << "TWO" << endl; }
};
struct Three : Number {
    virtual void f() { cout << "THREE" << endl; }
};
Number* oneCreator() { return new One(); }
Number* twoCreator() { return new Two(); }
Number* threeCreator() { return new Three(); }
int main() {
    Number *two = Number::getByName(string("two"));
    two->f();
    return 0;
}

尝试一些比之前的投球更简单的东西,并假设getByName不经常使用不支持的值调用。

template <typename TYPE>
Number * factory()
{
    return new TYPE();
}
static const std::map<std::string, Number*(*)()> factories =
{
    {One::name, factory<One>},
    {Two::name, factory<Two>},
    {Three::name, factory<Three>}
};
static Number* getByName(const std::string &name) 
{
    try
    {
        return factories.at(name)();
    }
    catch (std::out_of_range &e)
    {
        // Log factory called with bad name as a hint that the programmer 
        // should check calling code.
        return nullptr;
    }
}

但是,如果使用坏名字的调用是预期的用例,那么困难的方法要比处理异常便宜得多。

static Number* getByName(const std::string &name) 
{
    auto factory = factories.find(name);
    if (factory != factories.end())
    {
        return factory->second();
    }
    return nullptr;
}

最新更新