Я пытаюсь записать класс, который содержит несколько станд.:: векторы как элементы данных, и обеспечивают подмножество интерфейса вектора для доступа к ним:
class Mesh
{
public:
private:
std::vector<Vector3> positions;
std::vector<Vector3> normals;
// Several other members along the same lines
};
Главное, которое можно сделать с сеткой, добавляют положения, normals и другой материал к нему. Чтобы позволить подобному STL способу получить доступ к Сетке (добавьте от массивов, других контейнеров, и т.д.), я играю с идеей добавить методы как это:
public:
template<class InIter>
void AddNormals(InIter first, InIter last);
Проблема, от того, что я понимаю шаблонов, эти методы должны будут быть определены в заголовочном файле (кажется, имеет смысл; без конкретного типа итератора компилятор не знает, как сгенерировать объектный код для очевидной реализации этого метода).
Это - на самом деле проблема? Моя реакция пищеварительного тракта не состоит в том, чтобы обойти липкие огромные блоки кода в заголовочных файлах, но мой C++ немного ржав с не много опыта STL вне игрушечных примеров, и я не уверен, какая "приемлемая" практика кодирования C++ находится на этом.
Существует ли лучший способ выставить эту функциональность при сохранении подобного 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;
};
и затем выставьте экземпляры их на Сетке вместо этого, но это начинает сильно пахнуть немного сверхразработкой :P Любой совет значительно ценится!
эти методы должны быть определены в файле заголовка
Они должны быть определены в файле заголовка , так что если они используются, то они ' re доступен в единице перевода, в которой создается шаблонная функция. Если вас беспокоит слишком много шаблонов в файлах заголовков, замедляющее компиляцию единиц перевода, которые используют Mesh, но фактически не используют эту функцию шаблона, вы можете переместить реализацию в отдельный файл заголовка. Немного усложняет жизнь клиентам, решая, следует ли включать "жирный" заголовок класса или нет, но на самом деле это несложно.
В качестве альтернативы, для этого конкретного примера вы можете определить итератор вывода для Mesh, который добавляет Normals. Затем клиенты со своими произвольными итераторами могут:
std::copy(first, last, mymesh.normalAdder());
Единственный заголовок, который им нужен с кодом шаблона в нем, - это
, который, вполне возможно, у них уже есть.
Чтобы сделать это самостоятельно, объект, возвращаемый normalAdder ()
, должен перегрузить operator ++ ()
и operator * ()
, который сам должен вернуть прокси-объект (обычно * this
), который реализует operator = (const & Vector3)
. Это добавляется к вектору нормалей. Но все это не является шаблоном и может быть реализовано в вашем .cpp файле.
В этом примере normalAdder ()
может просто вернуть std :: back_inserter (this.normals);
, шаблон из
.
Что касается того, нужно ли вам об этом беспокоиться - я думаю, что когда время компиляции увеличивается, это чаще происходит из-за ненужных зависимостей, а не из-за небольших фрагментов кода шаблона в заголовках. Некоторые крупные проекты, похоже, требуют радикальных мер, но лично я не работал с более чем 100 файлами или около того.
Я бы сказал, просто прикусите пулю и сделайте чистый, читаемый API / заголовок.
Идея Стива о возврате итератора вывода остроумна, но она будет нелогичной для клиентов вашего класса. Когда кто-то хочет добавить нормали, он будет думать «где же метод добавления нормалей», а не «как мне получить нормальный итератор вывода». (Если только вы не находитесь в очень магазине, поддерживающем 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 );
}
- Действительно ли это проблема? Моя интуиция подсказывает, что не стоит засовывать огромные куски кода в заголовочные файлы, но мой C++ немного заржавел. у меня мало опыта работы с STL за пределами игрушечных примеров, и я не уверен, что "приемлемая" практика кодирования на C++ это.
Я бы задал вопрос, нужна ли вам такая универсальность? Помните, что STL был написан, чтобы быть чрезвычайно универсальным для всех. Ваш код предназначен для вас и вашей команды, для решения очень конкретной проблемы. Негенеративный, специфичный для конкретной проблемы интерфейс будет работать отлично и будет более понятен людям из вашей команды/области проблемы.
В противном случае... то, что вы указали, отлично подходит, если вам нужен такой уровень общности. Вы позволили принимать в качестве аргумента любой тип итератора. Нет ничего плохого в том, что у вас есть на первый взгляд. Это может быть очень полезно.