条件变量声明
Conditional variable declaration
我来自Python,我在c++中管理类型有一些问题。在Python中,我可以这样做:
if condition_is_true:
x=A()
else:
x=B()
和在程序的其余部分中,我可以使用x而不必关心x的类型,只要我使用具有相同名称和参数的方法和成员变量(A和B不一定具有相同的基类)。现在在我的c++代码中,类型A对应于
typedef map<long, C1> TP1;
and B to:
typedef map<long, C2> TP2;
地点:
typedef struct C1
{
char* code;
char* descr;
int x;
...
}
和
typedef struct C2
{
char* code;
char* other;
int x;
...
}
C1和C2有相似的成员,在我所说的代码部分,我只需要使用具有相同名称/类型的
我想这样做:
if (condition==true)
{
TP1 x;
}
else
{
TP2 x;
}
c++中正确的方法是什么?
提前致谢
如果在编译时知道该条件,则可以使用std::conditional
。这在通用代码中很有用。
typedef std::conditional<
std::is_pointer<T>::value
, TP1
, TP2
>::type map_type;
map_type x;
(其中测试是组成的;这里我们测试T
是否是指针类型)
如果直到运行时才知道条件,则需要某种形式的动态多态性。在c++中,这种多态性的典型实例是subtyping, boost::variant
,或者在紧要关头,boost::any
。你应该选择哪一个以及如何应用它取决于你的总体设计;我们知道的还不够。
*: very likely not to boost::any
.
你有几个选择。如果C1和C2都是POD类型,则可以使用联合,它允许访问公共初始序列:
struct C1 {
// ....
};
struct C2 {
// ...
};
union TP {
C1 c1;
C2 c2;
};
union TP x;
std::cout << x.c1.code; // doesn't matter if `code` was written via c1 or c2.
请注意,为了保持初始序列"公共",您确实需要更改名称,以便第二个成员(descr
/other
)在两个版本的结构体中具有相同的名称。
如果它们不是pod,你可以使用继承来给你一个通用的类型。
然而,c++并没有直接对应Python著名的"duck typing"。虽然模板提供了类型擦除(至少在某种程度上),但您最终会得到与Python中所做的相反的结果。在处理变量时,两种类型之间不会发生变化,而是允许代码处理具有相同语法的两种不同类型。然而,这是不同的,因为它要求编译器能够在编译时解析用于任何特定模板的实际类型,而不仅仅是在运行时。如果您确实需要在运行时解析类型,那么模板可能不起作用——您可能需要使用联合或基类。
如果您确实需要两个不同的类型,最好的方法是(假设类相似并且有一些相似的成员函数)有一个抽象类,比如CBase(参见http://www.cplusplus.com/doc/tutorial/polymorphism/),然后定义这个抽象类的两个子类C1和C2。
现在你的代码可以这样写:CBase *x;
if (condition) {
x = new C1();
} else {
x = new C2();
}
如果你不能将C1和C2抽象成一个共同的抽象类,那么你就需要两个不同的变量,condition
就像你的标志一样,使用它你可以知道哪个变量已经被填充,哪个结构要使用。
尽管可能有一些方法可以做到这一点,但正如Damon提到的,它们大多很棘手,而且不可维护。
建议使用模板函数。您真正想要的是访问不同类的相同成员/函数。在模板函数中,只要该类型提供模板中使用的操作,就可以访问"通用类型"的对象。
例如,在您的示例中,您可以简单地将公共部分提取到模板函数中,如下所示:
struct TP1
{
// common part
int i;
int j;
// different part
float f;
};
struct TP2
{
// common part
int i;
int j;
// different part
double d;
};
template<typename CType>
void f(CType a)
{
// as long as CType has variable i, j
cout << a.i << endl;
cout << a.j << endl;
}
int main(int argc, char* argv[])
{
bool choice;
// get a choice from console during runtime
cin >> choice;
if (choice)
{
TP1 x = {0, 0};
f(x);
}
else
{
TP2 x = {1, 1};
f(x);
}
return 0;
}
我认为你可以通过运行时多态性来做到这一点。
class C_Base { /*all common variables*/ } ;
class C1 : public C_Base { ... };
class C2 : public C_Base { ... };
typedef map<long, C_Base *> TP;
{
...
TP x;
if (condition)
/*use x as if values are C1 * */
else
/*other way round*/
}
为了使用两种不同的类型通过一个共同的变量,该类型必须有一个公共基类。因为你拥有的是两种不同的你不能改变的类型,没有公共基类的类型,你需要一些鸭子打字。在c++中,只有模板才使用duck输入:一种解决方案是将所有代码移到条件放入一个单独的函数模板中,将结果,然后写入如下内容:
if ( condition_is_true )
wrapped_code( A() );
else
wrapped_code( B() );
根据条件后面的代码,这可能是或多或少方便。
一个更通用的替代方法是创建类层次结构来包装地图。这个解决方案有点冗长,但非常简单:只需定义一个基用您想要的接口初始化,例如:
class Map
{
public:
virtual ~Map() {}
virtual std::string getCode( long index ) const = 0;
virtual std::string getDescr( long index ) const = 0;
virtual int getX( long index ) const = 0;
};
,然后是由它派生的模板:
template <typename T> // Constraint: T must have accessible members code, other and x
class ConcreteMap : public Map
{
std::map <long, T> myData;
public:
virtual std::string getCode( long index ) const
{
return myData[index].code;
}
virtual std::string getDescr( long index ) const
{
return myData[index].descr;
}
virtual int getX( long index ) const
{
return myData[index].x;
}
};
你的if
变成:
std::unique_ptr<Map> x = (condition_is_true
? std::unique_ptr<Map>( new ConcreteMap<C1> )
: std::unique_ptr<Map>( new ConcreteMap<C2> ));
你想做的在c++中是不可能的。c++中的变量有固定的类型,在编译时定义,不能在运行时改变类型。但是c++确实提供了多态性(看起来像动态类型),它允许派生类型实现基类功能,但是访问特定类型方法的唯一方法是将类型绑定到基类,如果您将类型绑定到派生类型,那么您只能调用该类型的实现*:
class Base
{
public: virtual void Func () = 0;
};
class C1 : public Base
{
public: virtual void Func () {}
};
class C2 : public Base
{
public: virtual void Func () {}
};
void SomeFunc ()
{
C1 *c1 = new C1;
C2 *c2 = new C2;
Base *b;
b = c1;
b->Func (); // calls C1::Func
b = c2;
b->Func (); // calls C2::Func
}
看起来b
已经改变了类型,但它的实际类型保持不变,它始终是Base *
,它只能被分配值c1
和c2
,因为它们共享一个公共基类Base
。
Base *b = new C1;
C1 *c1 = dynamic_cast <C1 *> (b);
但是它需要dynamic_cast
,这需要RTTI(运行时类型信息),它提供了一种方法来检查b
实际上指向C1
类型。如果您要执行以下操作:
Base *b = new C2;
C1 *c1 = dynamic_cast <C1 *> (b);
c1
将是空指针,而不是b
。但是C1和C2仍然必须有一个共同的基类才能工作。
class Base {....}
class C1 : public Base {....}
class C2 {....} // not derived from Base!
Base *b = new C2; // no way to convert C2 to Base!
C2 *c2 = new C2;
b = dynamic_cast <Base *> (c2); // still won't work, C2 not a Base
b = new C1; // fine, C1 is a Base
C1 *c1 = new C1;
b = c1; // again, fine
c1 = dynamic_cast <C1 *> (b); // fine, C1 is a derived type of Base, this will work
c2 = dynamic_cast <C2 *> (b); // won't work, C2 is not a derived type of Base
如果C1和C2是相关的(比如,CSquare和circle),那么一个公共基类是有意义的。如果它们不相关(比如,CRoad和CFood),那么一个公共基类将无济于事(它可以做到,但它不是很合乎逻辑)。前一种方法(公共基类)在其他答案中有很好的描述。如果您需要执行后者,那么您可能需要重新考虑如何构建代码以允许您执行前者。
如果你能详细说明你想用x
做什么,这会有所帮助。既然x
是一个容器,你只是想做容器相关的操作吗?
- 当然,在c++中事情从来没有那么容易,有很多事情会混淆这个问题。例如,派生类型可以私下实现公共基类虚方法:
的例子:
class B
{
public:
virtual void F () = 0;
};
class C : public B
{
private:
virtual void F () { .... }
};
C *c = new C;
B *b = c;
b->F (); // OK
c->F (); // error
- 在疯狂的部分中声明变量
- 如何在C++中为高分辨率时钟声明变量?
- 在命名空间中声明变量,在 main 中定义它,使其对所有其他文件可见
- CUDA 的性能取决于声明变量
- 如何在不为其声明变量的情况下获取和使用常量值的地址?
- C++声明变量时自动类型推断而不初始化
- 在不同循环中多次声明变量的优点
- 奇怪的错误 C2131 与 constexpr 声明变量
- 是否可以在 "if" 语句中声明变量?
- 在python-ctypes中声明变量并传递给dll函数
- 在递归函数C++中声明变量
- 只有一个定义/声明时标头声明变量的多堆定义错误
- 奇怪的未声明变量编译器错误
- 我在C++程序中声明变量时遇到问题
- 在命名空间中声明变量
- C++ lambda 按值捕获,而无需更早声明变量
- 声明变量以保存字符串列表时的内存分配
- 如何声明C 变量应突变
- 为什么允许在开关语句中声明变量?但不是声明 初始化
- 在同一命名空间中声明变量和函数是否出错?[C++]