boost源码学习之gemotry2:设计原理

boost gemotry

2.设计原理

假设你需要一个C++程序计算两点间的距离,你可能会定义一个结构体:

struct mypoint
{
    double x, y;
};

和一个函数,包含这个算法:

double distance(mypoint const& a, mypoint const& b)
{
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

非常简单,它是可用的,但不是通用的。对于库来说,它必须进一步设计。上述设计只能用于笛卡尔坐标系中的二维点、结构mypoint(而不是其他结构)。通用库应能够计算距离:

  • 对于任何point类或结构,而不仅仅是此mypoint类型

  • 在两个以上的维度上

  • 对于其他坐标系,例如地球上方或球体上

  • 点与线之间或其他几何图形组合之间

  • 以比双精度更高的精度

  • 避免平方根:我们通常不想这样做,因为这是一个相对昂贵的函数,而比较距离则没有必要

在本节和后续章节中,我们将逐步使设计更通用。

2.1使用模板

距离函数可以变成一个模板函数。这很简单,可以计算除mypoint之外的其他点类型之间的距离。我们添加了两个模板参数,允许输入两种不同的点类型。

template <typename P1, typename P2>
double distance(P1 const& a, P2 const& b)
{
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return std::sqrt(dx * dx + dy * dy);
}

这个模板版本稍微好一点,但不多。

考虑一个C++类,其中成员变量受到保护…这样的类不允许直接访问x和y成员。所以,这一段很短,我们继续。

2.2使用Traits

我们需要采用一种通用方法,允许任何点类型作为距离函数的输入。我们将使用traits系统添加几个级别的间接寻址,而不是访问x和y成员。然后,该函数变为:

template <typename P1, typename P2>
double distance(P1 const& a, P2 const& b)
{
    double dx = get<0>(a) - get<0>(b);
    double dy = get<1>(a) - get<1>(b);
    return std::sqrt(dx * dx + dy * dy);
}

此调整后的距离函数使用一个通用get函数,用维度作为模板参数,以访问点的坐标。这将转到traits系统,定义如下:

namespace traits
{
    template <typename P, int D>
    struct access {};
}

然后专门用于mypoint类型,实现一个名为get的静态方法:

namespace traits
{
    template <>
    struct access<mypoint, 0>
    {
        static double get(mypoint const& p)
        {
            return p.x;
        }
    };
    // same for 1: p.y
    ...
}

调用traits::access<mypoint,0>::get(a)现在返回x坐标。很好,不是吗?对于库中经常使用的函数来说,它太冗长了。我们可以通过添加额外的免费函数来缩短语法:

template <int D, typename P>
inline double get(P const& p)
{
    return traits::access<P, D>::get(p);
}

这使我们能够为任何具有traits::access专门化的点调用get<0>(a),如本段开头的距离算法所示。因此,我们希望使用x()之类的方法来启用类,只要access结构有专门化,并且有一个静态get函数为维度0返回x(),为维度1和y()返回类似的x(),就可以支持这些类。

Traits?
C++ traits技术浅谈

2.3维度不可知论

现在我们可以计算2D中点之间的距离,任何结构或类的点。然而,我们也想要3D。因此,我们必须使其维度不可知。这使我们的距离函数变得复杂。我们可以使用for循环遍历维度,但是for循环有另一种性能,而不是最初直接添加坐标。但是,我们可以更多地使用模板,并使距离算法如下,更复杂,但对模板迷有吸引力:

template <typename P1, typename P2, int D>
struct pythagoras
{
    static double apply(P1 const& a, P2 const& b)
    {
        double d = get<D-1>(a) - get<D-1>(b);
        return d * d + pythagoras<P1, P2, D-1>::apply(a, b);
    }
};

template <typename P1, typename P2 >
struct pythagoras<P1, P2, 0>
{
    static double apply(P1 const&, P2 const&)
    {
        return 0;
    }
};

距离函数正在调用pythagoras结构,指定维度数:

template <typename P1, typename P2>
double distance(P1 const& a, P2 const& b)
{
    BOOST_STATIC_ASSERT(( dimension<P1>::value == dimension<P2>::value ));

    return sqrt(pythagoras<P1, P2, dimension<P1>::value>::apply(a, b));
}

上面提到的dimension,使用traits类定义:

namespace traits
{
    template <typename P>
    struct dimension {};
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haimianjie2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值