提问人:blipblop 提问时间:11/24/2016 最后编辑:blipblop 更新时间:11/25/2016 访问量:1229
在运行时有条件地定义将在 C++ 代码的其余部分中使用 3 个类中的哪一个
Conditionally define, during runtime, which of 3 classes will be used in the rest of C++ code
问:
这是我尝试在C++中做的事情。从我使用的外部库中,我有 3 个类,它们的公共函数是相同的。我想在运行时开始时决定在我的其余代码中使用哪一个,具体取决于用户的硬件配置。MyClass1
MyClass2
MyClass3
为了更好地说明我的意思,让我举一个我知道行不通的例子。如果可以有条件地在运行时定义一个,我试图实现的目标如下所示: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
MyClass1
MyClass2
MyClass3
但是,上面的代码当然不起作用,因为当从运行时开始时执行的函数计算其值时,然后抱怨这不是常量。这是有道理的,因为无法在运行时定义。x
std::conditional
x
typedef
所以,我的问题是:有没有办法实现我正在尝试做的事情(不是因为我知道它不能在运行时定义)?请记住,并且由图书馆外部提供,并且以我无法轻易更改它们的方式提供。typedef
MyClass1
MyClass2
MyClass3
答:
如果这三个都继承自一个公共类,则可以执行以下操作:
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 语句的时间,你可以创建一个静态方法来生成一个新的基类:
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;
评论
BaseClass
BaseClass
if-elseif-else
new MyClassWraper
BaseClass
x
我能看到的唯一解决方案是使用模板为您生成代码。模语法错误,只要您将 x 转换为编译器已知值,基于您的解决方案就会起作用。诀窍是将所有使用 typedef 的代码包装在具有整数作为模板参数的模板函数/类中,如下所示:std::conditional
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>();
}
现在,您已经将条件转换为在运行时进行评估的内容,但您已经编译了这两种情况的代码,并且可以根据自己的心意执行每种情况(或两者)。
这样做的缺点是将使用该类的任何内容制作成模板或由模板调用的内容。除了“调度”点之外,我建议在类型而不是整数上成为模板(参见示例中的函数);这更好地表达了这样一个事实,即您的代码可以处理要实例化它的所有类型。如果要确保只能使用您感兴趣的三个类实例化函数,则应考虑使用 CRTP 模式(奇怪的是重复出现的模板参数)。doSomethingWithClass
另一方面,这有一个优点(相对于基于多态性的另一个答案),您可以使用堆栈而不是堆。
你可以在这里找到一个工作的例子。
希望对你有所帮助。
若要在编译时执行此操作,GetX 函数必须为 。
使用比较运算符也与模板语法冲突。您需要为小于和大于以下内容提供 consexpr 函数:constexpr
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;
如果无法生成 constexpr(因为该值是在运行时确定的),
则您正在寻找 Sum 类型。它们在函数式编程语言中很常见,C++ 现在以 std::variant 的形式支持库。GetX()
您的示例代码可以转换为以下内容:
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(); }
}
在这里,您将在运行时决定类型。
您可以继续使用模式匹配来评估所持有的类型。
评论
GetX()
TheClass
boost::get<MyClass2>(TheClass)
评论
typedef
typedef
typedef