Еще один любопытно повторяющийся шаблон - PullRequest
3 голосов
/ 22 сентября 2009
template <class Data, class Allocator = std::allocator<Node> >
class Node : public Data {
  // ...
};

Вопрос прост: как сделать приведенный выше код скомпилированным? Намерение состоит в том, чтобы дать Узлу возможность выделить другие Узлы (и предоставить распределитель по умолчанию).

Ответы [ 5 ]

6 голосов
/ 22 сентября 2009

Наконец я решил это! Решение состоит в том, чтобы отложить специализацию распределителя по умолчанию до тех пор, пока внутри класса, где Node уже определен:

template <class Data, template<class T> class TAllocator = std::allocator >
class Node : public Data {
  typedef TAllocator<Node> Allocator;
  // ...
};
6 голосов
/ 22 сентября 2009

Вы не можете написать это так:

template <class Data, class Allocator>
class Node;

template <class Data, class Allocator = 
  std::allocator<Node<Data, std::allocator<Node<...> >
class Node : public Data {
  // ...
};

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

struct DefaultAllocatorTag { };

template<typename Alloc, typename Node>
struct SelectAllocator {
  typedef Alloc type;
};

template<typename Node>
struct SelectAllocator<DefaultAllocatorTag, Node> {
  typedef std::allocator<Node> type;
};

template <class Data, class Allocator = DefaultAllocatorTag >
class Node : public Data {
  typedef typename SelectAllocator<Allocator, Node>::type 
    NodeAllocator;
};

Если это применимо, я бы определил распределитель в контейнере. Как это:

template<typename Data, typename Allocator = std::allocator<Data> >
struct Container {
  struct Node : Data { 
    typedef typename Allocator::template rebind<Node>::other NodeAllocator;
    ...
  };
  ...
};
2 голосов
/ 22 сентября 2009

Как насчет этого?:

#include <memory>

template<class Data>
class NodeImpl : public Data
{
};

template<class Data, class Allocator = std::allocator< NodeImpl<Data> > >
class Node : public NodeImpl<Data>
{
};

class MyAllocator
{
};

class MyDataClass
{
};

int main()
{
    Node<MyDataClass> node;

    Node<MyDataClass, MyAllocator> node_with_alloc;

    return 0;
}
1 голос
/ 22 сентября 2009

Вы не можете заставить его скомпилироваться - то, что вы пытаетесь создать, является "бесконечным" типом.

Давайте начнем с того, что вы не можете использовать необоснованный шаблон класса в качестве аргумента шаблона. Поэтому вам нужно передать Node в std :: allocator, например:

template <class Data, class Allocator = std::allocator<Node<Data, Something> > > 
class Node ...

Однако, что бы это было? Ну, std :: allocator

Хитрость в том, что распределители обязаны выделять не только аргумент шаблона, но и любой другой тип. Объявите свой класс как

template <class Data, class Allocator = std::allocator<Data> > class Node ...

Затем создайте распределитель для таких узлов:

typename Allocator::rebind<Node>::other nodeAllocator(myDataAllocator)

Этот пост vcblog о распределителях может помочь, хотя он слишком сфокусирован на итераторах.

0 голосов
/ 22 сентября 2009

Другое решение. Этот кажется более типичным. То есть. реализации вектора и интеллектуального указателя используют нечто подобное. Идея заключается в частном наследовании от распределителя:

template <class Data, template <class N> class Allocator = std::allocator>
class Node : public Data, private Allocator<Node<Data, Allocator> > {
  // ...
};

Бонус в том, что в декларации наследования мы уже можем использовать Node.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...