template <typename T>Здесь:class Node: public vector< Node <T> > { T data; public: Node(T _data): data(_data) {} T & operator*() { return data; } template <typename F> void for_each(F f) { for( size_t i = 0; i < this->size(); ++i ) (*this)[i].for_each(f); f(data); } };
- Node - шаблонный класс, представляющий вершину, параметризуется данными этой вершины.
- Фокус, о котором речь: vector< Node
<T> > - базовый класс, представляющий собой вектор вершин. - Конструктор и оператор разыменования.
- Функция обхода в глубину.
Всю основную функциональность по выделению-освобождению памяти, навигации и прочему берет на себя вектор!
Вектор также можно заменить на параметр шаблона, но толку в общем с гулькин хуй, а лаконичность теряется.
Как этим пользоваться:
typedef Node<size_t>Выхлоп у этой конструкции вот такой:Ns; Ns n(10); // объявляем вершину n.push_back(20); // добавляем ей детку n.push_back(25); // ещё одну n[0].push_back(30); // теперь добавляем внучка n[0].push_back(40); // и ещё одного n[0][1] = 50; // можно редактировать данные вершины вот так cout << "N " << *n << "\n"; // можно получать данные вершины вот так cout << "N " << *n[0] << "\n"; // а вот так данные детки cout << "N " << *n[0][1] << "\n"; // а вот так данные внучка n.for_each( cout << var("Node: ") << _1 << "\n" ); // а вот так можно обойти всех зараз // (include boost::lambda, если чо)
N 10 N 20 N 50 Node: 30 Node: 50 Node: 20 Node: 25 Node: 10
Пиздато, не правда ли?
А теперь для снятия интеллектуального напряжения предлагается
Как определить в С++, имеет ли заданный тип определённый operator()?
Ответ настолько полон трюками, что внутренний Александреску пробудился и пишет
Сначала ответ:
template <typename Type>
class has_member
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin {
void operator()(){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};
Рассмотрим, как это работает.
1. Здесь определены два типа. Содержимое их значения не имеет. Имеют значение лишь их размеры. Размеры должны быть различными, неважно, кто больше, но различными. Почему - см. ниже.
class yes { char m;};
class no { yes m[2];};
2.Здесь определена первая структура-помощник. Единственное, что с ней можно сделать - это вызвать её operator(). Зачем она нужна - см. ниже.
struct BaseMixin {
3. Вторая структура-помощник. Она является порождением нашего входного типа Type и первого помощника.
struct Base : public Type, public BaseMixin {};
Её интересной особенностью является то, что:
1. если у Type нет оператора(), то у Base он однозначно определён - потому что он есть у BaseMixin
2. если же у Type есть оператор(), то у Base он определён уже неоднозначно - потому что происходит путаница между определением оператора() в Type и в BaseMixin. Этим мы воспользуемся чуть позже
4. Третья структура помощник. Не умеет вообще ничего. Но является шаблоном с секретом
template <typename T, T t> class Helper{};
Вот в чём её секрет: у шаблона два параметра, которые связаны зависимостью: первый должен являться типом второго (или что тоже самое - второй должен являться объектом класса, указанного в первом параметре)
5. Кульминация:
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0);
Что здесь такое. Это шаблонная функция, принимающая два аргумента:
U* - указатель на некий тип
Helper<void (BaseMixin::*)(), &U::operator()>* - указатель на структуру Helper, определённую в п.4.
Структура Helper, как мы помним, может быть инстанцирована только если два её шаблонных параметра связаны отношением is-a (т.е. второй является объектом первого). Рассмотрим детально наши параметры.
5.1. void (BaseMixin::*)() - это указатель на функцию-член класса BaseMixin. (Функция ещё и без параметров должна быть, но здесь это неважно)
5.2. &U::operator() - а это уже указатель на перегруженный operator() класса U.
В результате выходит так:
если U::operator() является членом класса BaseMixin, то Helper успешно создаётся, а вслед за ним успешно создаётся и функция deduce.
В противном же случае.. не, не ошибка. Происходит нечто, что лучше всего характеризуется аббревиатурой SFINAE. Детали легко гуглятся, но если коротко - то компилятор ищет ещё какой-нибудь deduce. И находит:
6. Вот он, deduce на все случаи жизни. Между скобок у него многоточие, оно же элипсис, что значит, что он съест любые параметры. (За всеядность компилятор даёт ему наинизший приоритет, что важно).
static yes deduce(...);
Нельзя не отметить, что у наших двух deduce разный тип возвращаемого значения. То, что тела функций не определены, совершенно неважно - их никто не будет запускать.
И, наконец, развязка:
7.
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
Здесь много скобок, поэтому пойдём из самого нутри:
(Base*)(0) - это обычный ноль, но приведённый к указателю на класс Base
deduce((Base*)(0)) - увидев это, компилятор пытается инстанцировать deduce для типа Base*
Он смотрит на определение из п.5:
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0);
всемто U у нас оказывается Base, и, соответственно реализация функции выглядит так:
static no deduce(Base*, Helper<void (BaseMixin::*)(), &Base::operator()>* = 0);
Второй параметр имеет значение по умолчанию 0, поэтому его отсутствие не смущает компилятор.
Интереснее другое. Вот это:
&Base::operator()
Если вспомнить п.3, то это выражение имеет однозначный смысл только если у класса Type нет своего operator(). Т.е. Base::operator() эквивалентно BaseMixin::operator().
В таком случае условие создания структуры Helper выполняется (т.к. указатель на BaseMixin::operator() является указателем на функцию-член BaseMixin), структура Helper успешно <s>создаётся</s> реализуется, а следом за ней реализуется и функция deduce.
Если же у Type есть оператор(), то компилятору приходится реализовывать второй, всеядный deduce из п.6.
Ещё на одну скобку вверх:
sizeof(deduce((Base*)(0)))
Здесь мы должны получить размер возвращаемого функцией deduce значения. Фокус в том, что компилятор, как и читатель программы, способен узнать этот размер, не запуская эту функцию. Он просто смотрит на возвращаемый тип в сигнатуре функции да и считает его размер.
Вот и всё. В наличии следующее явление.
Если у Type нет оператора(), то тип deduce = no, а если есть - то yes.
Размеры у них тоже разные.
Сравниваем и возвращаем.
struct A { void operator() (int ) {}; };
struct B { int i; };
cout << "A " << has_member<A>::result << "\n";
cout << "B " << has_member<B>::result << "\n";
Получаем
А 1
B 0
Метода подходит и для определения наличия других членов, и других операторов. Мощная техника, в общем. Можно заdefinеить требуемый элемент и вызывать проверку для кого угодно.
Правда, можно нарваться на всякое типа "operator[] должен иметь ровно один аргумент", но это преодолимая.
Преодолимая хуйня.
Функциональная схема №1:

т.е. вот:
template <typename T> struct faka {
typedef T fakatype;
};
struct globalfaka {};
void main()
{
struct localfaka {};
faka<globalfaka> f1; // так можно
faka<localfaka> f2; // а так нельзя
}
Причин этому нетЪ.
Почему?
Во избежание.
operator= для любого класса всегда есть, независимо от того, определён ли он программистом. Если не определён - компилятор генерирует дефолтный.
Посему, во избежание неоднозначностей типа вот этой:
struct A {
int i;
A(int _i): i(_i) {};
}
A a1(100), a2(666);
a2 = a1; // теперь a2.i равно 100
A& operator=( A& dst, A& src) {
dst.i = src.i + 1;
}
a2 = a1; // теперь a2.i равно 101
Нельзя. Хотя по-моему с ним было бы пиздаче.
В С++ запрещено задавать для шаблонной функции шаблонные параметры по умолчанию.
Т.е. нельзя написать:
template <class A, class B = void*> bool f( A a, B b = 0) { blablabla; }
Если было бы можно, то вызов f(1) сгенерировал бы функцию
bool f(int a, void * b = 0)
При этом аналогичный шаблон класса создать можно.
Штрауструп говорит, что если приспичило, то надо писать так:
template <class A, class B> bool f( A a, B b ) { blablabla; }
template <class A> bool f( A a, void *b = 0) { blablabla; }
Проблема в том, что blablabla в результате дублируется
Штраус сам признаёт:
The prohibition of default template arguments for function templates is a misbegotten remnant...
The restriction seriously cramps programming style...