#c #templates #graph #boost-graph
#c #шаблоны #График #boost-график
Вопрос:
Моя проблема заключается в следующем. Я изучаю C , написав библиотеку graph, и хочу использовать как можно больше общих методов программирования; следовательно, ответ на мой вопрос с помощью «use 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’ в невзвешенном случае), но это вынудило бы меня использовать определенный тип веса для невзвешенного класса edge, поэтому это странно пахнет. Я имею в виду, что шаблон должен быть
template <class T>
class edge {
public:
...
virtual T get_weight(void) { return T(1); }
}
что не совсем удобно для пользователя или, по крайней мере, сбивает с толку, поскольку вы не ожидаете, что должны быть задействованы какие-либо веса.
BGL использует get()
функцию для получения веса; Я мог бы написать функцию, которая возвращает 1 или weight
в edge_T
зависимости от edge
, но меня беспокоит то, что происходит, когда один выводится из weighted_edge
или,,,? Если кто-то пишет:
template <class T>
inline T get_weight(edge amp; e) { return T(1); }
template <class T>
inline T get_weight(weighted_edge amp; e) { return T(e.weight); }
что произойдет, если передать производный класс? Существует ли механизм C , который выбирал бы «более близкий» базовый класс из этих двух?
Комментарии:
1. 1 если вы предоставите минимальный рабочий образец, который люди могут расширить с помощью требуемой вами функциональности. В общем, чтобы заставить Boost Graph распознавать ваши структуры, нужно адаптировать и добавлять признаки, но мы не можем показать, как это делается, пока не увидим, что там есть.
2. PS. вы пытаетесь использовать BGL или пытаетесь избежать его?
3. Я думаю, что проблема была изложена достаточно хорошо, и код объясняет все со стороны кодирования. В любом случае, я пытался избежать BGL — я упомянул об этом как о чем-то, чему я пытался научиться, но с треском провалился :).
Ответ №1:
Спасибо за ответ, сехе; Я нашел оптимальное решение для своей проблемы. Он заключается в написании двух функций,
template <class T>
inline T get_weight(edge const amp; e)
{ return T(1); }
template <class T>
inline T get_weight(weighted_edge const amp; e)
{ return T(e.weight); }
Таким образом, когда я пишу алгоритм кратчайшего пути, он может запрашивать вес любого из этих двух классов или любых производных от них, что важно для меня, потому что позже я, возможно, захочу добавить свойства к базовым классам ребер (например, цвета и т.д.). Следовательно, Когда я пишу
class my_edge : public edge { ... };
my_edge e;
и при использовании get_weight(e)
я получу поведение для невзвешенного края. Создание шаблонов для типа edge здесь не помогло бы, потому что оно не смогло бы использовать предписанное поведение для всех классов, происходящих от edge
, и отличать это от поведения для weighted_edge
.
Комментарии:
1. Рад, что вы нашли рабочее решение, к тому же простое! Я предполагаю, что я усложнил требования, потому что ваш ‘template by return type’ на самом деле ничего не делает: это просто странный способ написания приведения (
get_weight<double>(e)
точно такой же, как(double) get_weight(e)
)2. Я перехожу на C с хардкорного C .. так что, я думаю, это ближе к моему обычному
(T) e
:). Причина создания шаблонов наT
заключается в том, что невзвешенный вариант, скорее всего, захочет использовать целочисленные типы для подсчета стоимости пути, поэтому имеет смысл иметь возможность создавать шаблоны для него, а не просто использовать double / float везде.
Ответ №2:
Заранее: я предполагаю, что вы подумали о создании getWeight()
виртуального метода в базовом edge
классе (и заставили реализацию по умолчанию возвращать 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 edgeamp; e) const
{ return result_type(1); }
};
template <> struct get_weight_impl<weighted_edge>
{
typedef typename result_type int;
result_type operator()(const weighted_edgeamp; e) const
{ return result_type(e.weight); }
};
}
Обновление 1 Вы могли бы использовать
result_of<edge::weight>
(boost / TR1) илиdecltype(edge::weight)
(C 0x), чтобы избежать жесткого кодированияresult_type
typedefs. Это было бы истинной индукцией.Обновление 2 Чтобы получить перегрузку для
weighted_edge constamp;
«обслуживания» производных типов ребер, а также применить немного магии type_trait:
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 Eamp; 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 Eamp; 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 Eamp; 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 = amp;e;
std::cout << "edge:t" << get_weight(*ep) << std::endl;
weighted_edge* wep = amp;we;
std::cout << "weighted_edge:t" << get_weight(*wep) << std::endl;
wep = amp;de;
std::cout << "bogus:t" << get_weight(*wep) << std::endl;
}
Комментарии:
1. Я немного озадачен этим решением, потому что у вас шаблон на типе edge. В любом случае, я выяснил, что лучшим решением моей конкретной проблемы действительно является то, что я написал в исходном сообщении, функция, шаблонизированная для возвращаемого типа и перегруженная для разных типов ребер.
2. Поскольку я не понял назначения ваших шаблонов возвращаемого типа, я предположил, что вы хотите вывести возвращаемый тип, что вы можете сделать, используя мое решение. Я обновил свой ответ, чтобы показать, как использовать type_traits для принятия любых производных классов
weighted_edge
точно так же