[c++]шаблонная функция шаблонного класса
Хочу упрятать кастования static_cast и reinterpret_cast внутрь шаблонной функции предка, чтобы в потомке обращаться к полям-указателям предка функциями, возвращающими указатель на потомка и ссылку на указатель на потомка.
Упрощённый пример кода, который я хочу и который не компилируется:дык поставь коммент на той строчке, где не компилируется. приведи полную ошибку от компилятора, версию компилятора
дыкв режиме телепатии: http://ideone.com/FZkaYc
поставь слово template перед вызовом шаблонного метода next
обновил пост
пишу на плюсах уже 3 года, а всё время натыкаюсь на что-то, чего раньше не знал.
недавно вот узнал как ссылку на массив из функции возвращать, теперь это.
сейчас, кстати надо будет такую же штуку провернуть для функции, возвращающей массив из двух указателей по ссылке
недавно вот узнал как ссылку на массив из функции возвращать, теперь этонекоторые вещи лучше не знать вообще
template <typename PtrType>
PtrType (&children[ChildMax];
...
template <class T>
template <typename PtrType>
PtrType (&RedBlackTreeNode<T>::children[RedBlackTreeNode<T>::ChildMax]
{
return reinterpret_cast<PtrType (&)[ChildMax]>(nodeChildren);
}
Чего?
синтаксис у плюсов
Ну, типа, немного здорового бойлерплейта нам не повредит, нет? Сахар для девочек!
а зачем тебе вообще такая сложная схема нужна? не проще ли засунуть все нужные данные в структуру и подставить ее как параметр шаблона, вместо этого непонятного наследования?
template <typename T>
class Node
{
public:
typedef Node<T> Self;
typedef Self NodeType;
typedef NodeType* NodePtr;
T value;
NodePtr& next {
return nextNode;
}
NodePtr next const {
return nextNode;
}
private:
NodePtr nextNode;
};
template <typename T, class DerivedT>
class NodeConverter : public Node<T>
{
public:
DerivedT*& next {
return reinterpret_cast<DerivedT*&>(Node<T>::next;
}
DerivedT next const {
return static_cast<DerivedT*>(Node<T>::next;
}
};
template <typename T>
class NodeEx : public NodeConverter <T, NodeEx<T>>
{
public:
typedef NodeEx<T> Self;
typedef Node<T> Parent;
typedef Self NodeType;
typedef NodeType* NodePtr;
int additionalData;
void test
{
NodePtr t = this, n = 0;
t->next = n;
}
};
code:а что здесь написано?
template <typename PtrType>
PtrType (&children[ChildMax];
...
template <class T>
template <typename PtrType>
PtrType (&RedBlackTreeNode<T>::children[RedBlackTreeNode<T>::ChildMax]
{
return reinterpret_cast<PtrType (&)[ChildMax]>(nodeChildren);
}
при этом не хочется копировать код, т.к. потом придется поддерживать 2 актуальные версии, а кода довольно прилично, с учётом тех же итераторов получается.
на самом деле там так и есть как ты говоришь
template <class T, class Compare = std::less<T>, class TreeNodeType = RedBlackTreeNode<T> >
class RedBlackTree;
а потом реюз этого
template <class T, class Compare = std::less<T> >
class OrderStatisticTree : public RedBlackTree<T, Compare, OrderStatisticTreeNode<T> >
при этом
template <class T>
class OrderStatisticTreeNode : public RedBlackTreeNode<T>
{
public:
size_type& size;
size_type size const;
private:
size_type nodeSubtreeSize;
};
и
template <class T>
class RedBlackTreeNode
{
public:
typedef T value_type;
typedef RedBlackTreeNode<T> NodeType;
typedef NodeType * NodePtr;
public:
enum NodeColor {Red, Black};
enum ChildIndex { ChildFirst = 0, Left = ChildFirst, Right = ChildFirst+1, ChildMax = ChildFirst+2 };
public:
typedef NodePtr ChildrenNodeArray[ChildMax];
private:
T nodeValue;
ChildrenNodeArray nodeChildren;
NodePtr nodeParent;
NodeColor nodeColor;
...
T& value;
const T& value const;
NodePtr& left;
NodePtr left const;
NodePtr& right;
NodePtr right const;
NodePtr& parent;
NodePtr parent const;
...
};
если оставить RedBlackTreeNode как есть, с фукнциями left right parent то при инстанцировании потомка вылезают проблемы с приведением типов указателей в алгоритмах
и имплементация, которая эту ссылку кастит из массива из указателей NodePtr nodeChildren[ChildMax]
Curiously recurring template patternВот я не мог вспомнить, как он называется.
Curiously recurring template patternвот сто пудов, я еще помнил, что какая-то такая штука была, чтобы из рекурсии вырваться, но не вспомнил как она устроена и как называется.
я опять забыл, как тут раскраску синтаксиса прикрутить?
template<typename T, typename NodeType>
class _Node
{
public:
typedef NodeType* NodePtr;
NodePtr& next
{
return nextNode;
}
NodePtr next const
{
return nextNode;
}
private:
NodePtr nextNode;
};
template<typename T>
class Node: public _Node<T, Node<T> >
{
};
template<typename T>
class NodeEx: public _Node<T, NodeEx<T> >
{
public:
typedef NodeEx<T> Self;
typedef Node<T > Parent;
typedef Self NodeType;
typedef NodeType* NodePtr;
int additionalData;
void test
{
NodePtr t = this, n = 0;
t->next = n;
}
};
int main
{
NodeEx<double> x;
x.test;
return 0;
}
ага, так лучше, только я про этот способ забыл - когда класс наследуется от класса, инстанцированного самим собой.
Еще можно std::array использовать вместо обычного массива, тогда не будет такого ужаса с синтаксисом
std::array
он в с++03 есть? а то надо совместимость блюсти.
и как насчет эффективности?
с выносом функциональности согласен конечно.
эффективность такая же, как и у статического массива, динамическая память не выделяется, размер должен задаваться константой на этапе компиляции.
еще, ты планируешь увеличивать количество потомков? в красно-черном дереве вроде это не получится же, не? почему бы структуру не сделать для потомков, вместо массива? или std::pair использовать? или просто возвращать указатель на первый элемент массива?
мне это придется собирать для продакшена под древние RedHat'ы, там может быть чуть ли не gcc <3 версии
массив, а не два поля left и right использую потому, что все красно-чёрные алгоритмы сокращаются почти в 2 раза за счёт симметрии - вместо двух веток для left и right можно вычислить индексы вначале и использовать одну ветку алгоритма.
увеличивать количество потомков? в красно-черном дереве вроде это не получится же, не?вообще расширять необходимости нет, и ты прав, для красно-чёрного дерева точно не получится.
но для чсв хочется писать максимально общо, не закладываясь бинарность дерева где возможно.
вообще, имхо, по-хорошему надо выделить как минимум класс "бинарное дерево" с простой вставкой-поиском-удалением, на его основе сделать красно-чёрное, а на его - индексированное красно-чёрное.
но, как показывает практика, попытка сделать что-то совсем абстрактное приводит к большим проблемам в сражении с языком c++ или же к совершенно нечитаемому человеком, но при этом супер-абстрактному и структурному коду.
ну можно все равно возвращать указатель на первый элемент массива. Размер же известен массива.
ну это будет капитуляция перед c++
пишу на плюсах уже 3 годаgood for you
, а всё время натыкаюсь на что-то, чего раньше не знал.это шутка, да?
капитуляция перед с++ - это тот синтаксис для возвращения массивов, который ты используешь) а вернуть указатель - выглядит вполне нормально. Самое интересное, что дальнейший синтаксис в обоих вариантах будет одинаковый
Самое интересное, что дальнейший синтаксис в обоих вариантах будет одинаковыйочень смелое утверждение
namespace Detail
{
template <typename T, std::size_t N>
char (&ArraySizeDetector(T (&)[N][N];
}
#define STATIC_ARRAY_SIZE(a) (sizeof(Detail::ArraySizeDetector(a
блин, короче проще свой array написать, тем более там не нужны же никакие итераторы и т.п., только size и operator[]
он в с++03 есть? а то надо совместимость блюсти.Ну вроде, он не настолько сложен, можно самому слепить на коленке, а?
да, но зачем?
template <typename T, class NodeTypeT>
class BaseRedBlackTreeNode
{
public:
typedef T value_type;
typedef NodeTypeT NodeType;
typedef NodeType* NodePtr;
public:
enum NodeColor {Red, Black};
enum ChildIndex { ChildFirst = 0, Left = ChildFirst, Right = ChildFirst+1, ChildMax = ChildFirst+2 };
public:
typedef NodePtr ChildrenNodeArray[ChildMax];
typedef NodePtr const ChildrenNodeConstArray[ChildMax];
private:
ChildrenNodeArray nodeChildren;
NodePtr nodeParent;
NodeColor nodeColor;
T nodeValue;
public:
BaseRedBlackTreeNode( T v = T
NodePtr l = 0,
NodePtr r = 0,
NodePtr p = 0,
NodeColor c = Black );
T& value { return nodeValue; }
const T& value const { return nodeValue; }
NodeColor& color { return nodeColor; }
NodeColor color const { return nodeColor; }
bool isRed const { return nodeColor == Red; }
bool isBlack const { return nodeColor == Black; }
void paintItRed { nodeColor = Red; }
void paintItBlack { nodeColor = Black; }
NodePtr& left { return nodeChildren[Left]; }
NodePtr left const {return nodeChildren[Left]; }
NodePtr& right { return nodeChildren[Right]; }
NodePtr right const { return nodeChildren[Right]; }
NodePtr& parent { return nodeParent; }
NodePtr parent const { return nodeParent; }
ChildrenNodeArray& children { return nodeChildren; }
ChildrenNodeConstArray& children const { return nodeChildren; }
};
template <class T>
class RedBlackTreeNode : public BaseRedBlackTreeNode<T, RedBlackTreeNode<T> >
{
public:
typedef RedBlackTreeNode<T> Self;
typedef BaseRedBlackTreeNode<T, Self> Parent;
typedef T value_type;
typedef RedBlackTreeNode<T> NodeType;
typedef NodeType * NodePtr;
typedef typename Parent::NodeColor NodeColor;
public:
RedBlackTreeNode( T v = T
NodePtr l = 0,
NodePtr r = 0,
NodePtr p = 0,
NodeColor c = NodeType::Black );
};
template <class T>
class OrderStatisticTreeNode : public BaseRedBlackTreeNode<T, OrderStatisticTreeNode<T> >
{
private:
typedef OrderStatisticTreeNode<T> Self;
typedef BaseRedBlackTreeNode<T, OrderStatisticTreeNode<T> > Parent;
public:
typedef typename Parent::value_type value_type;
typedef Self NodeType;
typedef NodeType * NodePtr;
typedef typename Parent::NodeColor NodeColor;
typedef typename Parent::ChildIndex ChildIndex;
typedef size_t size_type;
public:
OrderStatisticTreeNode( T v = T
NodePtr l = 0,
NodePtr r = 0,
NodePtr p = 0,
NodeColor c = Parent::Black );
size_type& size;
size_type size const;
private:
size_type nodeSubtreeSize;
};
если вы руками реализуете красно-черное дерево, то вы делаете что-то не так
скачайте реализацию или заюзайте уже существующую!
да, но зачем?потому что в используемом тобой синтаксисе для возвращения массивов без поллитры не разобраться
я так понимаю, данные вопросы являются следствием запрос на супер-контейнер
именно
если вы руками реализуете красно-черное дерево, то вы делаете что-то не такПочему же? Контейнер не хуже других, но отсутствует в stl. Я уже даже из него сделал обертку в виде мапы, и заменил в рабочем проекте мапу на свою - пару ошибок выловил ну и убедился что мапа работает - совсместимость на уровне кода оказалась 100%.
скачайте реализацию или заюзайте уже существующую!
нормальных реализаций, подходящих по лицензии не нашлось, одни пионерские поделки вида RB-дерево от инта, да еще и с ошибкой в балансировке.
скопипастить из stl его внутреннее красночёрное дерево оказалось сложнее чем написать свое, да и подход сомнительный, можно случайно затащить что-то ненужное, да и из гнутого stl тырить моветон, насколько я понимаю, а из sgi stl, из которого можно тырить оказалось слишком сложно. вообще из любой целостной библиотеки шаблонов тяжело тырить что-либо, оно по зависимостям подтягивает много постороннего, да и разбираться с каждой такой деталью надо.
это потому что тип элемента массива шаблонный был, а там это нафиг не нужно. переделал как надо, теперь функция не шаблонная и через typedef массив выглядит не хуже любого другого возвращаемого типа.
скопипаститьвсе отлично копипастится, реализаций пруд-пруди
первая ссылка из гугла
http://kaba.hilvi.org/pastel/pastel/sys/redblacktree.htm
далее просто допиливаем как пожелаем
кстати, очень рекомендую для подобных Node-овых контейнеров поддерживать внешние аллокаторы.с их помощью можно прилично ускорить работу этих контейнеров
но я гуглил не Augmented Red Black Tree, а просто Red Black Tree и выдача была куда как плачевнее.
не думал, что есть готовая имплементация не просто красно-чёрного дерева, а еще и прямо дополненного как мне надо.
короче пипец. можно было не мучиться.
ну это будет капитуляция перед c++не перед плюсами, а перед собственным снобизмом. Вот если твои друзья могут разобраться в твоем коде на плюсах быстрее, чем за час, а потом через месяц снова разобраться в том же коде быстрее, чем за 5 минут, вот это круто. А остальное похуй.
чет я не понял, вообще в плюсах же запрещено возвращать статические массивы по значению?
так я же по ссылке
Оставить комментарий
elenangel
Хочу упрятать кастования static_cast и reinterpret_cast внутрь шаблонной функции предка, чтобы в потомке обращаться к полям-указателям предка функциями, возвращающими указатель на потомка и ссылку на указатель на потомка. Необходимо для переиспользования кода дерева - есть базовый узел, есть имплементация дерева у которого тип узла - параметр шаблона. В узле указатели на предков-потомков типа Предок* разумеется, мне нужно их использовать как Потомок*, при этом доступ к ним через геттеры, возвращающие ссылку.Упрощённый пример кода, который я хочу и который не компилируется:
не компилируется вот эта строка, g++
говорит