C++ 编译时一个类的子类列表

Pet*_*rNL 5 c++ templates compilation metaprogramming type-traits

我正在考虑如何创建从模板基类派生的所有类的列表。

首先我想要一个模板基类:

template <typename T>
class Base
{
public:
    Base() {};
    virtual ~Base() {};
};
Run Code Online (Sandbox Code Playgroud)

以及一个继承自模板基类的类:

class Foo : public Base<Foo >
{
public:
    Foo () {};
    virtual ~Foo () {};
};
Run Code Online (Sandbox Code Playgroud)

可能还有任意数量的其他子类,例如Foo. 结果应该看起来像这样:

std::vector<std::string> allTemplates = Base<Base>::getAllTemplateClasses();
Run Code Online (Sandbox Code Playgroud)

我的问题是是否可以在编译时创建所有子类的列表?魔术应该发生在基础班上,或者在儿童班上花费很少的努力。

我之前一直在思考不同的方向。首先我认为可以使用constexpr. 就像每个子类都需要一个带有签名的静态函数:

constexpr static std::string name() { "Foo";}
Run Code Online (Sandbox Code Playgroud)

或者认为也许可以使用元编程并创建一个编译时列表,例如使用 Template-Meta 的编译时数据结构示例。这里的问题是我不知道模板创建的头部。

接下来我正在考虑使用宏并构建一个枚举结构,就像这样枚举结构扩展一样。所以我找不到解决这个问题的办法,我想问你是否有可能?

编辑:

说清楚:我想要一个子对象的列表,而不需要创建它们。

Pet*_*rNL 4

有了这个漂亮的后静态构造函数和一个与此类似的已有答案,我能够找到解决方案。神奇之处在于静态构造函数。首先,我必须创建一个包含子类的容器并添加子类:

//base.h
std::set<std::string> &get_objects();
void add_object(const char *name);
Run Code Online (Sandbox Code Playgroud)

以及实施:

// base.cpp

std::set<std::string> &get_objects()
{
    static std::set<std::string> theSet;
    return theSet;
}

void add_object(const char *name)
{
    get_objects().emplace(name);
}
Run Code Online (Sandbox Code Playgroud)

所以现在我们必须创建一个静态类,将字符串添加到列表中。它类似于后静态构造函数:

//base.h
class StaticClassType {
public:
    StaticClassType(const char *name) {
        // Notify when the static member is created
        add_object(name);
    }
};
Run Code Online (Sandbox Code Playgroud)

基类是一个模板类,它创建“StaticClassType”的静态对象。C++ 保证静态初始化在 main() 调用之前完成。

//base.h
template<typename T>
class Base {
protected:
    // Static member in a template class
    static StaticClassType m;
    Base()
    {
        (void)m;
    }
};
Run Code Online (Sandbox Code Playgroud)

如果没有下一行,则m不会创建:

template<typename T>
StaticClassType Base<T>::m = StaticClassType(typeid(T).name());
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建两个类和主类:

class TestClass1 : public Base<TestClass1> {
public:
    TestClass1() :Base() {}
};

class TestClass1 : public Base<TestClass1> {
public:
    TestClass1() :Base() {}
};

int main()
{
    std::set<std::string> &test = get_objects();
    for(auto str : test)
        std::cout << str.c_str() << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出没有任何对象的任何构造:

class TestClass1
class TestClass2
Run Code Online (Sandbox Code Playgroud)

有一个人想照顾。我们必须m在某个地方使用。否则,编译器将优化代码并删除m. 我通过编写构造函数调用来强制执行此行为:

TestClass1() :Base() {}
Run Code Online (Sandbox Code Playgroud)

希望你喜欢它,这不是真正的编译时间,但我们通过编写构造函数并使用基类作为父类,无需执行任何操作即可获得对象列表。

  • 虽然这是解决您问题的一个很好的解决方案,但这实际上并不是一个“编译时”解决方案。该列表是在运行时构建的,即使正如您所说,它在 main 执行之前运行。 (3认同)