c++和通用图距离算法

C++ and generic graph distance algorithm

本文关键字:距离 算法 c++      更新时间:2023-10-16

我的问题如下。我正在通过编写图形库来学习c++,并希望尽可能多地使用泛型编程技术;因此,通过"使用BOOST"来回答我的问题对我没有帮助;事实上,我试着通过BOOST的代码来寻找我的问题的答案,但这是一个令人羞愧的经历,因为我甚至不能弄清楚某些函数在哪里定义;对我这个水平的人来说,c++的水平太高了。

也就是说,我的库是按以下方式模板化的:

class edge { ... };
template <class edge_T>
class node { ... };
template <class edge_T, class node_T>
class graph { ... };

,我通过使用从边或节点派生的类来创建更复杂的图,因此加权边类将简单地为

template <class T>
class weighted_edge : public edge {
   public:
     T weight;
   ...
};

现在的问题是我想在这个结构上实现一个算法来计算两个顶点之间的最短距离。我可以很容易地写出其中的两个,一个用于加权边,一个用于未加权边,但变化很小:一个将访问weighted_edge(或派生类)的成员字段,另一个将假定为幺正权。

是否有一种方法可以做到这一点,以便我可以只使用一段代码来处理这两种情况?

一个解决方案是使用一个成员函数edge::get_weight(),它将返回权重(或'1'在未加权的情况下),但这将迫使我使用一个特定的权重类型的边缘类是未加权的,所以它闻起来很有趣。我的意思是,模板应该是

template <class T>
class edge {
   public:
     ...
     virtual T get_weight(void) { return T(1); } 
}

并不完全是用户友好的,或者至少是令人困惑的,因为您不希望涉及任何权重。

BGL使用get()函数获得权值;我可以写一个函数,根据edge_T返回1或weight,但我关心的是,当一个人从edgeweighted_edge派生时会发生什么?如果有人写:

template <class T>
inline T get_weight(edge & e) { return T(1); }
template <class T>
inline T get_weight(weighted_edge & e) { return T(e.weight); }

如果传递一个派生类会发生什么?是否有一种c++机制可以从这两个基类中选择"更接近"的基类?

谢谢你的回复,sehe;我找到了问题的最佳解决方案。它是写两个函数,

template <class T>
inline T get_weight(edge const & e)
{ return T(1); }
template <class T>
inline T get_weight(weighted_edge const & e)
{ return T(e.weight); }
这样,当我编写最短路径算法时,它可以请求这两个类中的任何一个的权重或任何派生的,这对我来说很重要,因为我可能想要稍后向基本边缘类添加属性(如颜色等)。因此,当我写 时
class my_edge : public edge { ... };
my_edge e;

并使用get_weight(e),我将获得未加权边缘的行为。边缘类型的模板在这里没有帮助,因为它不能在edge之后的所有类上使用规定的行为,并将其与weighted_edge的行为区分开来。

Up front:我假设您已经考虑过在基础edge类中使getWeight()成为虚拟方法(并使默认实现返回1)。我意识到这种方法在灵活性上的局限性,只是想检查一下。


因为我不理解你的返回类型模板的目的,我假设你想推断返回类型,你可以使用我的解决方案。

get_weight选择正确实现的通常方法是使用模板专门化(注意,你所展示的代码是根据返回类型专门化的;根据定义,编译器永远不会推导出此类型):

namespace detail
{
    template <class Edge> struct get_weight_impl;
    template <> struct get_weight_impl<edge>
    {
        typedef typename result_type int;
        result_type operator()(const edge& e) const
            { return result_type(1); }
    };
    template <> struct get_weight_impl<weighted_edge>
    {
        typedef typename result_type int;
        result_type operator()(const weighted_edge& e) const
            { return result_type(e.weight); }
    };
}

Update 1您可以使用result_of<edge::weight> (boost/TR1)或decltype(edge::weight) (c++ 0x)来避免硬编码result_type类型。这是真正的归纳法。

更新2为了使weighted_edge const&的重载'service' 派生的边缘类型也应用了一点type_trait的魔力:

http://ideone.com/AqmsL

struct edge {};
struct weighted_edge : edge          { virtual double get_weight() const { return 3.14; } };
struct derived_edge  : weighted_edge { virtual double get_weight() const { return 42; } };
template <typename E, bool is_weighted>
struct edge_weight_impl;
template <typename E>
struct edge_weight_impl<E, false>
{
    typedef int result_type;
    int operator()(const E& e) const { return 1; }
};
template <typename E>
struct edge_weight_impl<E, true>
{
    // typedef decltype(E().weight()) result_type; // c++0x
    typedef double result_type;
    result_type operator()(const E& e) const 
    { 
        return e.get_weight();
    }
};
template <typename E>
    typename edge_weight_impl<E, boost::is_base_of<weighted_edge, E>::value>::result_type 
        get_weight(const E& e)
{
    return edge_weight_impl<E, boost::is_base_of<weighted_edge, E>::value>()(e);
}
int main()
{
    edge e;
    weighted_edge we;
    derived_edge de;
    std::cout << "--- static polymorphism" << std::endl;
    std::cout << "edge:t"          << get_weight(e) << std::endl;
    std::cout << "weighted_edge:t" << get_weight(we) << std::endl;
    std::cout << "derived_edge:t"  << get_weight(de) << std::endl;
    // use some additional enable_if to get rid of this:
    std::cout << "bogus:t"         << get_weight("bogus") << std::endl;
    std::cout << "n--- runtime polymorphism" << std::endl;
    edge* ep = &e;
    std::cout << "edge:t"          << get_weight(*ep) << std::endl;
    weighted_edge* wep = &we;
    std::cout << "weighted_edge:t" << get_weight(*wep) << std::endl;
    wep = &de;
    std::cout << "bogus:t"         << get_weight(*wep) << std::endl;
}