С++ CRTP и доступ к производным вложенным typedefs из базы

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

Фон

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

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

p.s., контейнеры для АСТ языка описания данных. Таким образом, содержащиеся элементы представляют собой небольшие типы данных, и 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 из базы. Это то, что clang должен сказать по этому поводу:

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;
}

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

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

Ответы

Ответ 1

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

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

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

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

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

Ответ 2

Расширяя @jpalecek, мы могли бы сделать аргумент шаблона аргументом по умолчанию. Но вам нужно включить С++ 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

Ответ 3

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

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