基于 C++ 中枚举值的专用类构造函数

Specialized class constructor based on enum value in C++

提问人:ChaoSXDemon 提问时间:7/29/2023 最后编辑:ChaoSXDemon 更新时间:7/29/2023 访问量:98

问:

我需要创建一个描述一组数学形状的类。假设我想要 、 和 。对于这些,我将创建一个来表示它们,并希望用户传入他们想要的形状类型并提供正确的输入。例如:SphereCylinderAABBOBBenum

// Shapes.h
enum ShapeType { Sphere, Cylinder, AABB, OBB, Count};

class Shape {
public: 
  Shape(ShapeType type, const float r, const float hl, const Vector3& minExtend, const Vector3& maxExtend);

private:
  ShapeType m_Type;
  float m_Radius;       // for Sphere and Cylinder end caps
  float m_halfLength;   // for Cylinders
  Vector3 m_minExtend;  // for AABB or OBB
  Vector3 m_maxExtend;  // for AABB or OBB
  Entity m_Parent;      // has position, orientation and scale; 
}

不理想的是,在创建 的实例时,用户被迫提供所有变量,即使他们不需要它。例如,Sphere 不会关心半长。因此,我的问题是 - 有没有办法根据值来专业化我的构造函数?例如,下面的伪代码:Shapeenum

// Shapes.h
enum ShapeType { Sphere, Cylinder, AABB, OBB, Count};

class Shape {
public: 
  Shape(ShapeType type = Sphere, const float r);
  Shape(ShapeType type = Cylinder, const float r, const float hl);
  Shape(ShapeType type = AABB, const Vector3& minExtend, const Vector3& maxExtend);
  Shape(ShapeType type = OBB, const Vector3& minExtend, const Vector3& maxExtend);

// ... same as above

注意:我真的不想在这里使用继承并创建一个形状系列,因为这个类实际上只是用于在发送到另一个进程之前保存一些原始数据。

C++ 模板

评论

0赞 Yunnosch 7/29/2023
如果将球体扩大到椭球体,则需要三个半径。如果将圆柱体加宽为椭圆棱柱,则需要两个半径和半长。您如何看待总是要求三个长度,但提供一种方便的输入方式“与以前相同”的想法?或者可以问三个值,每次都应该是“长度、与以前相同的长度、半径还是与以前相同的半径”;从而消除了球体、椭球体、棱柱、立方体、二次棱柱或盒子之间的选择;同时提供更多选择,几乎没有更多的输入需求。
0赞 ChaoSXDemon 7/29/2023
我知道该设计无法适应所有可能的形状,这没关系。我只想支持这些,这对现在有好处。除非你有一个非常好的:)
1赞 Ahmed AEK 7/29/2023
你知道 std::variant 吗?这几乎就是您要尝试实现的内容。
0赞 gthanop 7/29/2023
您能否澄清一下为什么在这种情况下使用继承不适用?即为每种形状类型使用一个类,而不是一个类来表示它们。如果您只需要使用一个类,那么请原谅我错过了问题中的相应陈述。
0赞 ChaoSXDemon 7/29/2023
听起来不错,在问题中添加。总的来说,我只想保留可用于表示这些形状的简单字段。它们不需要任何其他功能,因此继承将是一个很大的矫枉过正。

答:

3赞 fabian 7/29/2023 #1

如果提供形状类型作为参数,则无法执行此操作。毕竟给了

float r = 5;
ShapeType shape = getRandomShape(); // determine the shape to use via random number generation
Shape shape(shape, r);

编译器无法判断是否是 ;该值在运行时确定。shapeShapeType::Sphere

如果始终直接提供常量,则有一些方法可以使用制造商参数实现与此类似的操作,该参数为对象提供模板参数,以便编译器能够使用重载解析确定正确的构造函数。ShapeType

template<ShapeType>
struct ShapeTypeTag
{};

inline constexpr ShapeTypeTag<ShapeType::Sphere> SphereType;

class Shape {
public: 
    Shape(ShapeTypeTag<Sphere>, const float r);
    Shape(ShapeTypeTag<Cylinder>, const float r, const float hl);
    Shape(ShapeTypeTag<AABB>, const Vector3& minExtend, const Vector3& maxExtend);
    Shape(ShapeTypeTag<OBB>, const Vector3& minExtend, const Vector3& maxExtend);
};

...

Shape shape(SphereType, 5);

我实现这一点的首选方法是创建单独的静态成员函数以创建每个形状类型:Shape

class Shape {
    template<ShapeType>
    ShapeTypeTag
    {
    };  

    Shape(const float r);
    Shape(const float r, const float hl);
    Shape(ShapeTypeTag<AABB>, const Vector3& minExtend, const Vector3& maxExtend);
    Shape(ShapeTypeTag<OBB>, const Vector3& minExtend, const Vector3& maxExtend);

public:

    static Shape createSphere(float r)
    {
        return r;
    }

    static Shape createCylinder(float r, float hl)
    {
        return { r, hl };
    }

    static Shape createAABB(const Vector3& minExtend, const Vector3& maxExtend)
    {
        return { ShapeTypeTag<ShapeType::AABB>{}, minExtend, maxExtend };
    }

    static Shape createOBB(const Vector3& minExtend, const Vector3& maxExtend)
    {
        return { ShapeTypeTag<ShapeType::OBB>{}, minExtend, maxExtend };
    }
};

...

Shape shape = Shape::createSphere(5);

评论

0赞 Retired Ninja 7/29/2023
不久前看到这个问题,回来说几乎同样的话。作为该类的理论用户,我更喜欢名称告诉我我正在创建什么的函数,而不必猜测要使用哪个构造函数以及它需要什么参数。
0赞 gast128 7/29/2023
对于此解决方案,还可以将 std::integral_constant 与原始枚举结合使用来获取不同的类型。
0赞 ChaoSXDemon 7/30/2023
要是我的调用者能调用这些静态方法就好了!由于代码库限制,我认为我必须使用构造函数,我真的很喜欢您的标记方法。感谢您提供此解决方案!如果我可以使用静态方法一,我也希望这样做。