在运行时有条件地定义 3 类 中的哪一个将用于其余的 C++ 代码

Conditionally define, during runtime, which of 3 classes will be used in the rest of C++ code

这是我在 C++ 中尝试做的事情。从我使用的外部库中,我有 3 个 类、MyClass1MyClass2MyClass3,它们的 public 功能相同。我想在运行时开始时根据用户的硬件配置决定在我的其余代码中使用哪个。

为了更好地说明我的意思,让我举一个我知道行不通的例子。 如果 可以在运行时有条件地定义 typedef,我想要实现的 如下所示:

const int x = GetX(); //where GetX() is any function that calculates the value of x
typedef std::conditional<x > 0, MyClass1,
            std::conditional< x < 0, MyClass2,
                MyClass3>::type>::type TheClass;

所以在剩下的代码中,我只会引用 TheClass,这样无论它是别名 MyClass1MyClass2 还是 MyClass3.

但是,上面的代码当然不起作用,因为当 x 从运行时开始时执行的函数计算其值时,std::conditional 抱怨 x 是不是常数。这是有道理的,因为 typedef 无法在运行时定义。

所以,我的问题是:有没有办法实现我想要做的事情(不是 typedef 因为我知道它不能在运行时定义)?请记住,MyClass1MyClass2MyClass3 是由图书馆外部提供的,而且我无法轻易更改它们。

如果这三个都继承自一个共同的 class 你可以这样做:

class BaseClass
{
    virtual int GetSomething() = 0;
};

class Class1 : public BaseClass
{
    virtual int GetSomething() override
    {
        return 1;
    }
};

class Class2 : public BaseClass
{
    virtual int GetSomething() override
    {
        return 2;
    }
};

class TheClass : public BaseClass
{
    virtual int GetSomething() override
    {
        return 3;
    }
};


BaseClass* classInterface;

const int x = GetX(); //where GetX() is any function that calculates the value of x
if (x > 0) { classInterface = new MyClass1(); }
elseif (x < 0) { classInterface = new MyClass2(); }
else { classInterface = new TheClass(); }

printf(classInterface->GetSometing());

如果没有,您需要将它们包裹起来并执行如下操作:

class Class1
{
    int GetSomething()
    {
        return 1;
    }
};

class Class2
{
    int GetSomething()
    {
        return 2;
    }
};

class TheClass
{
    int GetSomething()
    {
        return 3;
    }
};

class BaseClass
{
    virtual int GetSomething() = 0;
};

class Class1Wrapper  : public BaseClass
{
    Class1 m_class;

    virtual int GetSomething() override
    {
        return m_class.GetSomething();
    }
};

class Class2Wrapper : public BaseClass
{
    Class2 m_class;

    virtual int GetSomething() override
    {
        return m_class.GetSomething();
    }
};

class TheClassWrapper : public BaseClass
{
    TheClass m_class;
    virtual int GetSomething() override
    {
        return m_class.GetSomething();
    }
};    

BaseClass* classInterface;

const int x = GetX(); //where GetX() is any function that calculates the value of x
if (x > 0) { classInterface = new MyClass1Wrapper(); }
elseif (x < 0) { classInterface = new MyClass2Wrapper(); }
else { classInterface = new TheClassWrapper(); }

printf(classInterface->GetSometing());

[编辑] ..如果你想节省重做 if 语句的时间,你可以创建一个静态方法来生成一个新的基数class:

static BaseClass* GetClass()
{
    BaseClass* classInterface;
    const int x = GetX(); //where GetX() is any function that calculates the value of x
    if (x > 0) { classInterface = new MyClass1Wrapper(); }
    elseif (x < 0) { classInterface = new MyClass2Wrapper; }
    else { classInterface = new TheClassWrapper; }
    return classInterface;
}

..然后这样称呼它:

BaseClass* classInterface = GetClass();

// Do something

delete classInterface;

我能看到的唯一解决方案是使用模板为您生成代码。模语法错误,如果您将 x 转换为编译器已知的值,则基于 std::conditional 的解决方案确实有效。诀窍是将所有使用 typedef 的代码包装在模板 function/class 中,该模板将整数作为模板参数,如:

template <int x>
void myMain(){
    using TheClass = typename std::conditional<x == 0, MyClass1, MyClass2>::type;

然后您要确保编译所有变体(在我的示例中为 0 和非零)并明确调用 myMain<0>()myMain<1>() ,如:

if(x == 0){
    myMain<0>();
}
else{
    myMain<1>();
}

现在您已将条件转换为在 运行 时评估的内容,但是您已经编译了两种情况的代码,并且您可以执行其中的每一个(或两者)以满足您的需求。

这有一个缺点,那就是将使用 class 的任何东西都变成模板或模板调用的东西。除了 "dispatching" 点之外,我建议在类型上而不是在整数上成为模板(参见示例中的函数 doSomethingWithClass);这更好地表达了这样一个事实,即您的代码可以使用您要实例化它的所有类型。如果你想确保你只能用你感兴趣的三个 classes 实例化函数,你应该考虑使用 CRTP 模式(奇怪的重复模板参数)。

另一方面,这有一个优点(相对于基于多态性的其他答案),您可以使用堆栈而不是堆。

您可以找到一个工作示例 here

希望对您有所帮助。

要在编译时执行此操作,GetX 函数必须是 constexpr。 使用比较运算符也与模板语法冲突。您将需要提供小于和大于的 conexpr 函数:

constexpr int GetX(){ return 0;}
constexpr bool IsGreater(int x, int y) { return x > y;}
constexpr bool IsLess(int x, int y) { return x < y;}

typedef std::conditional<IsGreater(GetX(),0), MyClass1,
            std::conditional<IsLess(GetX(),0), MyClass2,
                MyClass3>::type>::type TheClass;

如果你不能GetX() constexpr(因为值是在运行时决定的),
那么您正在寻找 Sum Type. They are quite common in functional programming languages and C++ now has library support in the form of std::variant

您的示例代码可以变成以下内容:

int main(){

  //a type that can be 1 of 3 other types
  std::variant<MyClass1,MyClass2,MyClass3> TheClass;

  //decide what type it should be at runtime.
  const int x = GetX();
  if      (x > 0) { TheClass = MyClass1(); }
  else if (x < 0) { TheClass = MyClass2(); }
  else            { TheClass = MyClass3(); }
}

在这里,您在运行时决定类型。
您可以继续使用模式匹配来评估持有的类型。