#c #templates #inheritance
#c #шаблоны #наследование
Вопрос:
Вот моя проблема: я написал два базовых класса: Wire и CircuitComponent. Эти два были достаточно похожи, чтобы быть производными от общего суперкласса, но нет. Wire может соединяться только с CircuitComponent, а CircuitComponent может соединяться только с wire. Реализации были идентичны, за исключением типа, поэтому, естественно, я подумал, что шаблоны были ответом.
Вот шаблон, и у меня есть класс Wire, который является производным от TwoTypeMesh<Wire, CircuitComponent>
и класс CircuitComponent, который является производным от TwoTypeMesh<CircuitComponent, Wire>
:
template <class thisType, class otherType>
class TwoTypeMesh {
std::set<otherType *> neighbors;
public:
void join(otherType * n){
if (neighbors.find(n) != neighbors.end()) {
return;
} else {
neighbors.insert(n);
n->join(this);
}
}
void disconnect(otherType * n){
if (neighbors.find(n) == neighbors.end()) {
return;
} else {
neighbors.erase(n);
n->disconnect(this);
}
}
};
Проблема в том, что я не могу заставить его скомпилироваться, он жалуется на строку с n->join(this)
причиной this
типа TwoTypeMesh<Wire, CircuitComponent>
(суперкласс Wire
), но join
определен только для wire
.
Моя лучшая теория на данный момент заключается в том, что я не должен создавать подклассы, возможно, typedef, но мне пока не удалось заставить это работать.
Комментарии:
1. может быть, typedef TwoTypeMesh<OtherType, ThisType> realOtherType; ?
Ответ №1:
Минимально инвазивный способ заставить ваш код компилироваться — это действительно использовать typedef и либо классы тегов, либо просто перечисления:
enum MeshType { MeshTypeWire, MeshTypeCircuitComponent };
template <MeshType thisType>
class TwoTypeMesh {
// calculate 'otherType' from 'thisType' (prevents usage mistakes):
static const MeshType otherType =
thisType == MeshTypeWire ? MeshTypeCircuitComponent :
/* else */ MeshTypeWire ;
std::set< TypeTwoMesh<otherType> *> neighbors;
public:
void join(TypeTwoMesh<otherType> * n){
if (neighbors.find(n) != neighbors.end()) {
return;
} else {
neighbors.insert(n);
n->join(this);
}
}
void disconnect(TypeTwoMesh<otherType> * n){
if (neighbors.find(n) == neighbors.end()) {
return;
} else {
neighbors.erase(n);
n->disconnect(this);
}
}
};
typedef TwoTypeMesh<MeshTypeWire> Wire;
typedef TwoTypeMesh<CircuitComponent> CircuitComponent;
Комментарии:
1. Законно ли использовать перечисляемое значение в качестве типа? Логично, что это имеет смысл, но я не понимаю, как на самом деле работает TypeTwoMesh<MeshTypeWire> (который перечисляется, что делает его int)…
2. @Alex: аргументами шаблона в C могут быть типы, но также и целые константы, которые являются значениями enum.
Ответ №2:
переместить join() за пределы класса:
void join(Wire amp;w, CircuitComponent amp;j);
void join(CircuitComponent amp;j, Wire amp;w);
возможно, вам потребуется сделать функции дружественными классу для доступа к закрытым элементам данных.
Комментарии:
1. Это означало бы просто вообще не использовать шаблоны по существу, и для этого мне все еще нужно иметь два почти идентичных определения функций.
2. Я думаю, вы слишком стараетесь избежать дублирования кода, даже в ситуациях, когда это будет работать не очень хорошо. Поддерживайте интерфейс к вашему классу в порядке, а затем дублируйте все, что требуется для правильных прототипов функций.
3. Я согласен с tp1 — интерфейс — это собака, реализация — хвост. Возможно, вы захотите ослабить свой интерфейс, потому что действительно сложно реализовать идеальный интерфейс, но небольшое дублирование кода, которого вы, возможно, позже выясните, как избежать, не представляет серьезной трудности. На первый взгляд, не могли бы вы избежать дублирования, просто имея
void join(CircuitComponent amp;j, Wire amp;w) { join(w,j); }
? Затем выполните работу по проверке и добавлению обоих друг к другу в качестве соседей, в первомjoin
.
Ответ №3:
Чтобы устранить вашу конкретную ошибку компиляции, вы должны иметь возможность static_cast this
to thisType*
в вызове n->join
.
Похоже, вы случайно заново изобрели CRTP: базовый класс шаблона, который принимает производный класс в качестве параметра шаблона. Просто никогда не наследуйте от TwoTypeMesh<T,U>
ни в каком классе, отличном от T , и сделайте TwoTypeMesh
конструкторы защищенными, чтобы предотвратить прямое создание экземпляра. Тогда вы можете быть уверены, что любой экземпляр TwoTypeMesh<T, something>
является подобъектом базового класса экземпляра T
T
(или производного класса static_cast
), и, следовательно, T*
to,, является допустимым.
Комментарии:
1. Могут ли
TwoTypeMesh
конструкторы быть частными, а затем использоватьfriend class T;
? Тогда вы гарантируете, что любой подкласс должен предоставить свой собственный тип в качестве первого параметра шаблона.2. @Ben: Я так думаю, это тоже часть трюка, который вы можете использовать, чтобы сделать класс не наследуемым-from.