C ++ CRTP и доступ к вложенным typedefs из базы - PullRequest
16 голосов
/ 13 ноября 2011

edit: Я добавлю сюда ссылку на github, когда закончу изменять свой дизайн для всех, кому это интересно.

Фон

Я заменяю boost::intrusive, intrusive_set своей собственной реализацией в виде 64-разрядных скомпилированных наборов вторжений 3 x 8-байтовых указателей на мои узлы контейнера.мой контейнер имеет ограничение в 2 ^ 16 узлов, поэтому я могу уменьшить его до 4 байтов на узел с помощью 2-х 16-битных смещенных ординалов (что в 6 раз меньше размера).

В приведенном ниже примере base является контейнером с навязчивым набором.Класс derived имеет std::vector<container_entry_type<entry_type> >.очевидно, что при таком уровне косвенности мне нужно иметь набор вложенных typedef в производных, на которые я хотел бы сослаться в base.

ps, контейнеры предназначены для AST языка описания данных.Содержащиеся элементы, следовательно, представляют собой небольшие типы данных и имеют размер 3 x 8 байт.Тем более что контейнеры используются для проверки наборов данных в тесных циклах.

Проблема изолирована

Я хочу добиться следующей семантики:

template<typename TWO>
class base
{
public:
  void foo(typename TWO::dummy & d);
};

template<typename DUMMY>
class derived
  : private base< derived<DUMMY> >
{
public:
  typedef DUMMY dummy;
};

struct tag{};

int main()
{
  derived<tag> foo;
}

Но я не могу получить доступ к вложенной typedef с базы.Вот что должен сказать по этому поводу Кланг:

main.cc: In instantiation of ‘base<derived<tag> >’:
main.cc:9:7:   instantiated from ‘derived<tag>’
main.cc:20:16:   instantiated from here
main.cc:5:8: error: no type named ‘dummy’ in ‘class derived<tag>’

Вместо этого я должен сделать:

template<typename type_key>
class traits
{
public:
  typedef type_key dummy;
};

template<typename TWO, typename type_key>
class base
{ 
public:
  void foo(typename traits<type_key>::dummy & d);
};

template<typename DUMMY>
class derived
  : private base< derived<DUMMY>, DUMMY >
{
public:
  typedef DUMMY dummy;
};

struct tag{};

int main()
{
  derived<tag> foo;
}

Это единственный способ достичь моего варианта использования?это только делает вещи намного более многословными.Я предполагаю, что производная может также быть производной от черт, чтобы сохранить некоторые нажатия клавиш.

Другой выбор - не использовать деривацию и связывать логику прямо с тем, что в настоящее время получено.Однако я бы хотел индивидуально пройти тестовую базу.

Ответы [ 3 ]

10 голосов
/ 13 ноября 2011

Другая возможность (которая может или не может спасти вас нажатия клавиш) будет не использовать вложенные типы производных классов в родительском в некоторых местах. Например. вместо

void foo(typename TWO::dummy & d);

вы бы использовали

template <class T>
void foo(typename T& d);

Для дополнительных очков вы можете использовать SFINAE, чтобы фактически ограничить T типами, допустимыми для исходного варианта. (Обратите внимание, что внутри вложенных шаблонов TWO::dummy может использоваться свободно - они создаются только после всего этого, включая derived, так что это работает. В наивной версии derived все еще не завершен в момент создания экземпляра base с его функциями-членами, у него нет ::dummy, поэтому он не работает)

5 голосов
/ 13 ноября 2011

Расширяя идею @ jpalecek , мы могли бы сделать так, чтобы этот аргумент шаблона принял аргумент по умолчанию.Но вам нужно включить C ++ 0x, чтобы получить это

#include <typeinfo>
#include <cstdio>

template<typename TWO>
class base
{
public:
    template <typename X = TWO>   // <-- (requires C++0x to have a default)
    void foo(typename X::dummy& d)
    {
        printf("%s\n", typeid(d).name());
    }
};

template<typename DUMMY>
class derived
  : public base< derived<DUMMY> >
{
public:
  typedef DUMMY dummy;
};

struct tag{};

int main()
{
  derived<tag> foo;
  tag t;
  foo.foo(t);       // <--- call the function like normal.
}

http://ideone.com/AXXdW

1 голос
/ 13 ноября 2011

Нет необходимости в классе traits. Вы можете просто использовать type_key непосредственно в base.

Однако нельзя избежать явной передачи типа в base. В момент создания экземпляра base компилятор еще не видел typedef в derived (точнее: класс derived еще не завершен - как это возможно, учитывая, что даже его базовый класс не существует еще).

...