c++ - 变量返回类型

标签 c++ boost variable-assignment

假设您有如下内容:

class Shape  // base class
{
private:
    bool degenerate, ill_defined;
    ...
public:
    bool isVoid  () { return false; }
    bool isCircle() { return false; }
    bool isPoint () { return false; }
    bool isPlane () { return false; }
    bool isSphere() { return false; }
    ...
};

class Void : public Shape {
    ...
}

class Plane : public Shape
{
public:
    bool isPlane() { return !degenerate && !ill_defined; }
    bool isVoid () { return ill_defined; }
    ...
    operator Void () throw() { 
        if (isVoid()) return Void(); 
        else throw ...; //some error
    }
    ...
}

class Point : public Shape {
private:
    double radius;
    ...
public:
    bool isPoint() { return !ill_defined; }
    bool isVoid () { return ill_defined; }
    ...        
    operator Void () throw() { ... }
    ...
}

class Circle : public Shape // similar to the rest

class Sphere : public Shape // similar to the rest

Plane 之间的交集和一个 Sphere可以是

  • 一个Circle (如果平面“穿过”球体)
  • 一个Point (如果平面“刚刚接触”球体)
  • 一个Void (如果球体完全位于平面上方或下方)

我想知道如何最好地定义和使用 Plane 之间的交集和一个 Sphere ,由于假设的返回类型

intersect(const Sphere& S, const Plane& P)

方法/自由函数在编译时未知。

我以前从未遇到过这种情况,所以我查找了一些可能的方法来做到这一点。我遇到了this question其中推荐boost::variant 。在我的情况下,这看起来像

boost::variant<Void, Point, Circle> intersection = 
    intersect(const Sphere& S, const Plane& P);

但这有三个缺点:

  1. 太丑了。
  2. 类似于 intersection.radius不能按原样使用,因为 PointVoid没有radius 。你必须做类似的事情

    if (intersection.isPoint()){
        ...
    }
    else if (intersection.isCircle())
    {
        // possibly cast to Point if degenerate, otherwise:
        double R = intersection.radius;
        ...
    }
    // etc.
    
  3. 实现所有这些形状的库的用户始终必须知道两个形状相交可以返回什么类型。也就是说,用户总是必须声明 boost::variant<scope::Void, scope::Point, scope::Circle> 类型的内容。这很复杂而且很丑陋。幸运的是,c++11 有 auto的关键字。或者,您可以使用像这样的成员

    class Sphere : public Shape
    {
        ...
    public: 
    
        boost::variant<scope::Void, scope::Point, scope::Circle>
            intersect_type;
    
        intersect_type intersect(const Plane& P);
    
        ...
    };
    

    这样我们就可以使用

    Sphere::intersect_type t = S.intersect(P);
    

    哪里SSphere 的一个实例和P Plane 的一个实例。但我们仍然必须单独处理所有可能的类型:

    if (intersection.isPoint()){
        ...
    }
    else if (intersection.isCircle()){
        intersection.radius;
    }
    // etc.
    

因此我们试图从用户那里消除的复杂性实际上仍然存在。

我觉得我错过了一些东西。也许有一种更聪明的方法来实现我的Shape基类?或者我应该创建一个单独的专用Intersect类(class)?对于这种情况,什么解决方案是最优雅、最高效、最有效的?

最佳答案

副手:

isXXXX()对我来说,谓词方法似乎有一种代码味道。你会这样做

  • if (dynamic_cast<Circle*>(shapePtr))通常与 RTTI
  • 或者使用variant::which()和/或 variant::type()区分变体的存储值

对于你的问题:

有几种可能的方法。

  1. 经典的面向对象方法是从 Shape 派生所有内容并始终返回 std::unique_ptr<Shape> (或类似)。

  2. 但是,显然,您可以执行现代 C++ 静态 OO,在这种情况下,您最终会得到类似于变体的东西。然后,您可以编写一个访问者来处理不同的情况:

(直播于 http://liveworkspace.org/code/bad329cb40d94a21531e1153f4c0877b )

#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/variant.hpp>
#include <boost/variant/static_visitor.hpp>

struct Shape 
{ 
    /*virtual*/ double getSurface() const { return 42.0; }  // TODO
};

struct Circle : Shape {};
struct Point : Shape {};
struct Rect : Shape {};

struct Nil {};

typedef boost::variant<Nil, Circle, Point, Rect> Intersect;

struct DescribeVisitor : boost::static_visitor<std::string>
{
    std::string operator()(Circle const& s) const {
        return std::string("Got a circle of ") + boost::lexical_cast<std::string>(s.getSurface());
    }

    std::string operator()(Rect const& s) const {
        return std::string("Got a rectangle of ") + boost::lexical_cast<std::string>(s.getSurface());
    }

    std::string operator()(Point const& s) const {
        return std::string("Got a point of ") + boost::lexical_cast<std::string>(s.getSurface()); // mmm bit funny :)
    }

    std::string operator()(Nil const&) const {
        return std::string("Got an empty intersection");
    }
};

std::ostream& operator<<(std::ostream& os, Intersect const& i)
{
    return os << boost::apply_visitor(DescribeVisitor(), i);
}

int main(int argc, const char *argv[])
{
    Intersect describe = Point();
    std::cout << describe << std::endl;

    describe = Rect();
    std::cout << describe << std::endl;

    describe = Circle();
    std::cout << describe << std::endl;
}

输出:

Got a point of 42
Got a rectangle of 42
Got a circle of 42

关于c++ - 变量返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12705506/

相关文章:

c++ - 带有 C++11 的 Visual Studio 2008

c# - 是否有开放源代码项目在无异常(exception)的情况下实现相同的功能?

c++ - 如何找到用于声明类的模板参数的数量?

c++ - Boost 序列化给出未定义的类型 'boost::STATIC_ASSERTION_FAILURE'

c++ - 模板赋值运算符 : valid C++?

c++ - #include 头文件已经包含在包含的头文件中是常见的做法吗?

c++ - 通过套接字接收 GET 请求

c++ - 为什么 boost::equals 要求范围是可复制的?

python - 如何在 Python 中将字典值分配给变量?

java - boolean 值 and= 赋值运算符含义