条件变量声明

发布于 2024-11-26 00:53:41 字数 768 浏览 5 评论 0原文

我来自 Python,在管理 C++ 中的类型时遇到一些问题。在Python中我可以做这样的事情:

if condition_is_true:
    x=A()
else:
    x=B()

在程序的其余部分我可以使用x而不关心x的类型,因为我使用具有相同名称和参数的方法和成员变量(A和B不一定有相同的基类)。 现在,在我的 C++ 代码中,类型 A 对应于

typedef map<long, C1> TP1;

,B 对应于:

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++ 中正确的方法是什么?

提前致谢

I'm coming from Python and I have some problem with managing types in c++. In Python I can do something like this:

if condition_is_true:
    x=A()
else:
    x=B()

and in the rest of the program I can use x without caring about the type of x, given that I use methods and member variables with the same name and arguments (not necessary that A and B have the same base classes).
Now in my C++ code type A corresponds to

typedef map<long, C1> TP1;

and B to:

typedef map<long, C2> TP2;

where:

typedef struct C1
{
    char* code;
    char* descr;
    int x;
...
}

and

typedef struct C2
{
    char* code;
    char* other;
    int x;
...
}

C1 and C2 have similar members and in the part of code I'm talkin of I only have to use the ones with the same name/type

I would like to do something like:

if (condition==true)
{
    TP1 x;
}
else
{
    TP2 x;
}

what is the correct approach in c++?

thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

不打扰别人 2024-12-03 00:53:41

如果条件在编译时已知,则可以使用 std::conditional。这在通用代码中很有用。

typedef std::conditional<
    std::is_pointer<T>::value
    , TP1
    , TP2
>::type map_type;
map_type x;

(其中测试是虚构的;这里我们测试 T 是否是指针类型)

如果直到运行时才能知道条件,则需要某种形式的动态多态性。 C++ 中此类多态性的典型实例是子类型、boost::variant 或当紧要关头时boost::any。您应该选择哪一个*以及如何应用它取决于您的总体设计;我们知道的还不够。

*:很可能不是boost::any

If the condition is known at compile-time, you can use std::conditional. This is useful in generic code.

typedef std::conditional<
    std::is_pointer<T>::value
    , TP1
    , TP2
>::type map_type;
map_type x;

(where the test is made-up; here we're testing whether T is a pointer type or not)

If the condition cannot be known until runtime, then some form of dynamic polymorphism is needed. Typical instances of such polymorphism in C++ are subtyping, boost::variant or when push comes to shove, boost::any. Which one you should pick* and how you should apply it depends on your general design; we don't know enough.

*: very likely not to be boost::any.

兮颜 2024-12-03 00:53:41

你有几个选择。如果 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 著名的“鸭子类型”直接对应的东西。虽然模板提供了类型擦除(至少在某种程度上),但您最终会得到与在 Python 中所做的相反的结果。您可以允许代码处理具有共同语法的两种不同类型,而不是在处理变量时发生两种类型之间的变化。然而,这是不同的,因为它要求编译器能够在编译时(而不仅仅是运行时)解析与任何特定模板一起使用的实际类型。

如果您确实需要在运行时解析类型,那么模板可能不起作用——您可能需要使用联合或基类。

You have a couple of choices. If C1 and C2 are both POD types, you could use a union, which allows access to the common initial sequence:

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.

Note that to keep the initial sequence "common", you really want to change the names so the second member (descr/other) has the same name in both versions of the struct.

If they're not PODs, you can use inheritance to give you a common type.

C++, however, doesn't have a direct counterpart to Python's famous "duck typing". While templates provide type erasure (to at least some degree), you'd end up with kind of the reverse of what you're doing in Python. Instead of the variation between the two types happening where you deal with the variable, you'd allow code to deal with two different types that had common syntax. This is different, however, in that it requires that the compiler be able to resolve the actual type being used with any particular template at compile time, not just run time.

If you really need to resolve the type at run time, then templates probably won't work -- you'll probably need to use a union or base class.

以可爱出名 2024-12-03 00:53:41

如果您确实需要两种不同的类型,最好的办法是(假设这些类相似并且具有一些相似的成员函数)有一个抽象类,例如 CBase (请参阅 http://www.cplusplus.com/doc/tutorial/polymorphism/)然后定义这个抽象类的两个子类C1和C2 。

现在您的代码可以编写如下:

CBase *x;
if (condition) {
  x = new C1();
} else {
  x = new C2();
}

如果您无法将 C1 和 C2 抽象为一个公共抽象类,那么您将需要两个不同的变量,并且 condition 的作用类似于您使用的标志稍后可以知道哪个变量已被填充以及要使用哪个结构。

If you really need two different types, the best thing to do would be (assuming the classes are similar and has some similar member functions) to have an abstract class, say, CBase (see http://www.cplusplus.com/doc/tutorial/polymorphism/) and then define two subclasses C1 and C2 of this abstract class.

Now your code can be written as follows:

CBase *x;
if (condition) {
  x = new C1();
} else {
  x = new C2();
}

In case you can not abstract C1 and C2 into a common abstract class, well, then you'll need two different variables and condition acts like your flag using which you can know later which variable has been populated and which structure to work with.

帥小哥 2024-12-03 00:53:41

尽管可能有一些方法可以做到这一点,但正如达蒙提到的那样,它们大多很棘手且不可维护。

我建议您使用模板功能。您真正想要的是访问不同类的相同成员/函数。在模板函数中,您可以访问“通用类型”的对象,只要该类型提供您在模板中使用的操作即可。

例如,在您的情况下,您可以简单地将公共部分提取到这样的模板函数中。

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;
}

Although there may be some ways to do it, they're mostly tricky and not maintainable, just as Damon mentioned.

I recommend you to use template function. What you really want is to access the same member/functions for different class. In template function, you can access the object of a "general type" as long as the type provides the operation you use in the template.

For example, in your case you can simply extract the common parts into a template function like this.

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;
}
蓝眸 2024-12-03 00:53:41

我认为你可以通过运行时多态性来做到这一点。

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*/
}

i think you can do it by runtime polymorphism.

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*/
}
北陌 2024-12-03 00:53:41

为了通过公共变量使用两种不同的类型,类型
必须有一个共同的基类。因为你拥有的是两个不同的
您无法更改且没有公共基类的类型,
你需要某种鸭子打字。在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> ));

In order to use two different types through a common variable, the types
must have a common base class. Since what you have is two different
types which you can't change, and which don't have a common base class,
you need some sort of duck typing. In C++, only templates use duck
typing: one solution would be to move all of the code after the
condition into a separate function template, to which you pass the
results, and then write something like:

if ( condition_is_true )
    wrapped_code( A() );
else
    wrapped_code( B() );

Depending on the code that actually follows the condition, this may be
more or less convenient.

A more general alternative is to create your class hierarchy to wrap the
maps. This solution is a bit verbose, but very easy: just define a base
class with the interface you want, say:

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;
};

, and then a template which derives from it:

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;
    }
};

Your if then becomes:

std::unique_ptr<Map> x = (condition_is_true
                          ? std::unique_ptr<Map>( new ConcreteMap<C1> )
                          : std::unique_ptr<Map>( new ConcreteMap<C2> ));
青瓷清茶倾城歌 2024-12-03 00:53:41

你想要做的事情在 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已经改变了类型,但它的实际类型保持不变,它始终是一个基数 * 并且只能是分配了值c1c2,因为它们共享一个公共基类Base。也可以采用另一种方式:

Base *b = new C1;
C1 *c1 = dynamic_cast <C1 *> (b);

但它需要 dynamic_cast 并且需要称为 RTTI(运行时类型信息)的东西,它为编译后的代码提供了一种检查 bb 的方法。 code> 实际上指向一个 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 和 CCircle),那么公共基类就有意义。如果它们不相关(例如,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

What you're trying to do is not possible in C++. Variables in C++ have a fixed type which is defined at compile time and they can't change type at run time. But C++ does provide polymorphism (which looks like dynamic types) which allows derived types to implement base class functionality, but the only way to access type specific methods is to have a type bound to the base class, if you have a type bound to the derived type then you can only call that type's implementation*:

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
}

It looks like b has changed type, but it's actual type has remained the same, it is always a Base * and it can only be assigned the value c1 and c2 because they share a common base class Base. It is possible to go the other way:

Base *b = new C1;
C1 *c1 = dynamic_cast <C1 *> (b);

but it requires the dynamic_cast and that requires something called RTTI (Run-Time Type Information) which provides the compiled code a way to check that b is actually pointing to a C1 type. If you were to do the following:

Base *b = new C2;
C1 *c1 = dynamic_cast <C1 *> (b);

c1 would be the null pointer, not b. But C1 and C2 must still have a common base class for this to work. This is not legal:

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

If C1 and C2 are related (say, CSquare and CCircle) then a common base class makes sense. If they are not related (say, CRoad and CFood) then a common base class won't help (it can be done, but it's not very logical). Doing the former (common base class) has been well described in the other answers. If you need to do the latter, then you may need to re-think how the code is structured to allow you to do the former.

It would help if you could expand on what you want to do with x. Since x is a container, do you just want to do container related operations?

  • Of course, things are never that easy in C++ and there are many things that can confuse the issue. For example, a derived type may implement a public base class virtual method privately:

Example:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文