在类列表上迭代

Iterating over a list of classes

本文关键字:迭代 列表      更新时间:2023-10-16

我希望能够迭代从共同祖先继承的类列表。

我想要的内容(像Python一样的语法是我来自的语言)的缩小版本:

const *Player *PLAYERS[3] = { *PlayerTypeOne, *PlayerTypeTwo, *PlayerTypeThree};
int outcome = 0;
for player in players {
    if (doThingWithPlayer((&player)(), some, other, variables) == true) {
        outcome++;
    }
}

如果这不是执行此类操作的首选方法,则非常欢迎有关我如何继续的建议。

我想避免的代码是:

int outcome = 0;
PlayerTypeOne player_one();
if doThingWithPlayer(player_one, some, other, variables){
    outcome++;
}
PlayerTypeTwo player_two();
if doThingWithPlayer(player_two, some, other, variables){
    outcome++;
}
PlayerTypeThree player_three();
if doThingWithPlayer(player_three, some, other, variables){
    outcome++;
}

您正在寻找工厂设计模式:

Player *create_by_name(const std::string &what)
{
   if (what == "PlayerTypeOne")
         return new PlayerTypeOne;
   if (what == "PlayerTypeTwo")
         return new PlayerTypeTwo;
   // ...
}

等等。您似乎也想做的是为每个子类的构造函数提供参数。

如果所有子类都采用相同的构造函数参数,这将变得琐碎:将参数传递给工厂,然后将它们转发给构造函数。

如果您需要支持构造函数的不同参数,这将变得更加复杂。我建议您启动小规模,并为您的对象实施一个简单的工厂,没有构造函数参数,或者只有几个对于所有子类别都相同的工厂。一旦有了基本原则,您就可以担心处理复杂的角案件。

然后,只有一系列班级名称,在数组上迭代并致电工厂。这应该具有与您的伪Python代码相似的结果。

c 没有内置的内省,因此您不能仅获得代表您的类的对象并使用它们创建实例。

您可以做的是使用元图:

// A list of types
template <class...> struct pack { };
// Calls f with one default-constructed instance of each T
template <class... Ts, class F>
void construct_each(pack<Ts...>, F &&f) {
    // Classic pre-C++17 expansion trick
    using ex = int[];
    (void)ex{(f(Ts{}), void(), 0)..., 0};
    // C++17 version
    // (void)(f(Ts{}), ...);
}
// ...
using Players = pack<PlayerTypeOne, PlayerTypeTwo, PlayerTypeThree>;
void foo() {
    int outcome = 0;
    construct_each(Players{}, [&](auto &&player) {
        if(doThingWithPlayer(player, some, other, variables))
            ++outcome;
    });
}

看到它活在Coliru