STLifying C ++ классы - PullRequest
       23

STLifying C ++ классы

5 голосов
/ 26 мая 2010

Я пытаюсь написать класс, который содержит несколько std :: vectors в качестве членов данных и предоставляет подмножество векторного интерфейса для доступа к ним:

class Mesh
{
public:
private:
  std::vector<Vector3> positions;
  std::vector<Vector3> normals;
  // Several other members along the same lines
};

Главное, что вы можете сделать с сеткой, это добавить в нее положение, нормали и другие вещи. Чтобы разрешить STL-подобный способ доступа к Mesh (добавление из массивов, других контейнеров и т. Д.), Мне нравится идея добавления таких методов:

public:
  template<class InIter>
  void AddNormals(InIter first, InIter last);

Проблема в том, что, насколько я понимаю, шаблоны должны определять эти методы в заголовочном файле (кажется, имеет смысл; без конкретного типа итератора компилятор не знает, как генерировать объектный код для очевидного реализация этого метода).

  1. Это на самом деле проблема? Моя внутренняя реакция не сводится к тому, чтобы вставлять огромные куски кода в заголовочные файлы, но мой C ++ немного заржавел с небольшим опытом STL за пределами игрушечных примеров, и я не уверен, что такое «приемлемая» практика кодирования C ++ в этом.

  2. Есть ли лучший способ раскрыть эту функциональность, сохранив STL-подобный универсальный аромат программирования? Одним из способов было бы что-то вроде этого:

(конечный список)

class RestrictedVector<T>
{
public:
  RestrictedVector(std::vector<T> wrapped)
    : wrapped(wrapped) {}

  template <class InIter>
  void Add(InIter first, InIter last)
  {
    std::copy(first, last, std::back_insert_iterator(wrapped));
  }

private:
  std::vector<T> wrapped;
};

, а затем вместо этого обнародовать их примеры в Mesh, но это начинает пахнуть небольшим перегрузом: P Любой совет очень ценится!

Ответы [ 3 ]

5 голосов
/ 27 мая 2010

эти методы должны быть определены в заголовочном файле

Они должны быть определены в заголовочном файле a , чтобы, если они используются, они были доступны в модуле перевода, где создается функция шаблона. Если вас беспокоит слишком большое количество шаблонов в заголовочных файлах, что замедляет компиляцию модулей перевода, которые используют Mesh, но на самом деле не используют эту функцию шаблона, тогда вы можете перенести реализацию в отдельный заголовочный файл. Немного усложняет жизнь клиентам, решая, включать ли заголовок класса «полный жир» или нет, но на самом деле это не сложно.

В качестве альтернативы, для этого конкретного примера вы можете определить выходной итератор для Mesh, который добавляет нормали. Тогда клиенты со своими произвольными итераторами могут сделать:

std::copy(first, last, mymesh.normalAdder());

Единственный заголовок, который им нужен с кодом шаблона в нем, - это <algorithm>, который, возможно, уже есть.

Чтобы сделать это самостоятельно, объект, возвращаемый normalAdder(), должен перегрузить operator++() и operator*(), который сам должен вернуть прокси-объект (обычно *this), который реализует operator=(const &Vector3). Это добавляет к вектору нормалей. Но все это не шаблонный код, который может быть реализован в вашем файле .cpp.

Снова в этом примере normalAdder() может просто вернуть std::back_inserter(this.normals);, шаблон из <iterator>.

Относительно того, нужно ли вам беспокоиться об этом - я думаю, что когда время компиляции идет ввысь, это происходит чаще из-за ненужных зависимостей, а не из-за небольших кусочков кода шаблона в заголовках. Некоторые крупные проекты, похоже, нуждаются в решительных мерах, но лично я не работал с чем-то более 100 файлами или около того.

2 голосов
/ 27 мая 2010

Я бы сказал, просто прикуси пулю и сделай чистый, читаемый API / заголовок.

Идея Стива вернуть выходной итератор умна, но она будет нелогичной для клиентов вашего класса. Когда кто-то хочет добавить несколько нормалей, он будет думать, «где метод добавления нормалей», а не «как получить нормальный итератор вывода». (Если вы не в очень магазине pro-STL.)

Требование определения реализации шаблонных методов в заголовке можно несколько смягчить, переместив их за пределы объявления класса.

class Mesh
{
    public:
        void    AddPosition     ( Vector3 const & position );
        void    AddNormal       ( Vector3 const & normal );

        template< typename InIter >
        void    AddPositions    ( InIter const begin, InIter const end );

        template< typename InIter >
        void    AddNormals      ( InIter const begin, InIter const end );

    private:
        std::vector< Vector3 > positions;
        std::vector< Vector3 > normals;
};

template< typename InIter >
void
Mesh::AddPositions< InIter >( InIter const begin, InIter const end )
{
    positions.insert( positions.end(), begin, end );
}

template< typename InIter >
void
Mesh::AddNormals< InIter >( InIter const begin, InIter const end )
{
    normals.insert( normals.end(), begin, end );
}
0 голосов
/ 26 мая 2010
  1. Это действительно проблема? Моя внутренняя реакция - не ходить торчащие огромные куски кода в заголовке файлы, но мой C ++ немного ржавый с небольшим опытом STL снаружи примеры игрушек, и я не уверен, что "приемлемая" практика кодирования на C ++ включена это.

Я бы задал вопрос, нужно ли вам иметь такую ​​универсальность? Помните, что STL был написан, чтобы быть чрезвычайно общим для потребностей каждого. Ваш код предназначен для вас и вашей команды, чтобы решить очень специфическую проблему. Неуниверсальный, специфичный для проблемы интерфейс будет работать нормально и будет более понятным для людей из вашей команды / проблемной области.

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

...