Почему `source()` библиотеки Boost Graph является глобальной функцией?

Я понимаю, что в универсальном программировании алгоритмы отделены от контейнеров. Таким образом, не имеет смысла реализовывать обобщенный алгоритм в качестве метода экземпляра (один и тот же алгоритм должен работать с несколькими конкретными классами; мы не хотим, чтобы все они наследуются от одного ABC, поскольку это приведет к экспоненциальному увеличению количества классов).

Но в случае с функцией source() в Библиотека Boost Graph, я не понимаю почему это глобальная функция, а не метод экземпляра класса графа.

Насколько я мог судить, прочитав исходный код BGL, source(e, g) необходимо знать детали реализации переданного ему графа и граничных объектов; недостаточно знать только их интерфейсы.

Итак, source() не является общим алгоритмом. Другими словами, ему необходимо знать конкретный класс экземпляра графа. Тогда почему бы не поместить его в тот же класс, что и метод экземпляра? Разве это не было бы намного чище/менее запутанно, чем создание глобальной функции, которую необходимо настраивать для каждого вызываемого класса?

ОБНОВИТЬ

Соответствующий исходный код:

  // dwa 09/25/00 - needed to be more explicit so reverse_graph would work.
  template <class Directed, class Vertex,
      class OutEdgeListS,
      class VertexListS,
      class DirectedS,
      class VertexProperty,
      class EdgeProperty,
      class GraphProperty, class EdgeListS>
  inline Vertex
  source(const detail::edge_base<Directed,Vertex>& e,
         const adjacency_list<OutEdgeListS, VertexListS, DirectedS,
                 VertexProperty, EdgeProperty, GraphProperty, EdgeListS>&)
  {
    return e.m_source;
  }


namespace boost {

  namespace  detail {

    template <typename Directed, typename Vertex>
    struct edge_base
    {
      inline edge_base() {} 
      inline edge_base(Vertex s, Vertex d)
        : m_source(s), m_target(d) { }
      Vertex m_source;
      Vertex m_target;
    };
  }
}

person max    schedule 19.04.2013    source источник
comment
Нет причин, по которым источник (a, b) не может быть специализирован на основе типов его параметров. Не все должно быть функцией-членом. Некоторые бесплатные функции можно считать частью интерфейса класса. Кроме того, может быть полезно использовать source() в качестве прокладки. Не читая и не понимая код (который недоступен в течение 2 кликов по вашим ссылкам), я не мог бы сказать вам об этом, поскольку я не использую библиотеку графиков, но их можно было бы рассмотреть. Кроме того, напишите напрямую разработчикам BGL и спросите об их дизайнерском решении. Я ожидаю, что для этого есть веская причина   -  person Pete    schedule 20.04.2013
comment
Есть ли причина, по которой это вас беспокоит?   -  person Pete    schedule 20.04.2013
comment
drdobbs.com/cpp/how-non-member- функции-улучшить-инкапсуляцию/   -  person Benjamin Lindley    schedule 20.04.2013
comment
@BenjaminLindley спасибо - это хорошее чтение.   -  person max    schedule 20.04.2013
comment
@Pete Мне кажется странным, что edge_base раскрывает своего m_source члена. Если это часть его общедоступного интерфейса, то почему даже с функцией source можно было бы просто позволить людям получить прямой доступ к m_source. Напишу разработчикам, просто подумал, может я что-то упускаю, потому что не знаком с C++.   -  person max    schedule 20.04.2013


Ответы (2)


source(e, g) не является общим алгоритмом. Это часть интерфейса, обычно называемая концепцией в C++. Причина того, что функция не является членом, заключается в том, что ее можно реализовать ненавязчиво.

Скажем, например, вы хотели, чтобы std::multimap реализовало концепцию IncidenceGraph. Если бы графическая библиотека требовала, чтобы source() была функцией-членом, вам не повезло, поскольку std::multimap ее не предоставляет.

person Paul Fultz II    schedule 20.04.2013
comment
Я уверен, что причина в этом. Но почему плохо наследовать от multimap и добавлять source в качестве функции-члена? - person max; 20.04.2013
comment
@max Это неплохо, просто это не решает проблему превращения std::multimap в IncidenceGraph - person Paul Fultz II; 21.04.2013
comment
Если я вас правильно понял, вы говорите о следующей ситуации. Предположим, мне дан объект data, который является экземпляром std::multimap. Я хочу интерпретировать data как граф, рассматривая ключевые значения как вершины графа, а его сопоставленные значения - как списки смежности. Я понимаю, почему здесь прекрасно работает подход, не связанный с функциями-членами. Но что произойдет, если я захочу рассматривать data как график, используя две разные интерпретации? Затем мне нужно написать два набора функций, не являющихся членами. Как избежать их столкновения друг с другом? Разве BGL не требует, чтобы все они находились в одном пространстве имен? - person max; 22.04.2013

В C++ следует предпочесть функции, не являющиеся членами, не являющиеся друзьями, где это возможно. Если source можно реализовать с точки зрения открытых членов классов, для которых он предназначен, он должен быть вне класса.

Это не имеет ничего общего с созданием универсальных алгоритмов; речь идет исключительно об уменьшении объема кода, который имеет доступ к внутреннему состоянию частных членов класса или может испортить их.

person Billy ONeal    schedule 19.04.2013
comment
Спасибо. Это действительно функция, не являющаяся дружественной, потому что объект, к которому она обращается, имеет только общедоступные члены (фактически это структура). Однако я не думаю, что понимаю, почему эти члены обнародованы. См. мой предыдущий комментарий. - person max; 20.04.2013
comment
@max это зависит от того, предназначен ли класс/структура для предотвращения нарушения инварианта. В этом случае, возможно, две вершины, которые содержит структура, не имеют реального инварианта, который необходимо поддерживать (т. е. это допустимо, если они являются одной и той же вершиной?). Если инварианта нет, то почему члены должны быть закрытыми? Последовательность? В качестве альтернативы, возможно, разработчики BGL просто не удосужились написать весь связующий код. - person Pete; 25.04.2013