C++ CRTP и доступ к вложенным typedefs из базы
редактировать: я помещу здесь ссылку на 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 с базы. Вот что по этому поводу должен сказать 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;
}
Это единственный способ достичь моего варианта использования? это только делает вещи намного более многословными. Я полагаю, что производная может также быть производной от черт, чтобы сохранить некоторые нажатия клавиш.
Другой выбор - не использовать деривацию и связать логику прямо с тем, что в настоящее время происходит. Тем не менее, я бы хотел индивидуально пройти тестовую базу.
3 ответа
Другой возможностью (которая может или не может спасти вас нажатия клавиш) будет не использовать вложенные типы производных классов в родительском в некоторых местах. Например. вместо
void foo(typename TWO::dummy & d);
вы бы использовали
template <class T>
void foo(typename T& d);
Для дополнительных очков, вы можете использовать SFINAE, чтобы реально ограничить T
к типам, допустимым для исходного варианта. (Обратите внимание, что внутри вложенных шаблонов, TWO::dummy
могут быть использованы свободно - они создаются только после всего этого, в т.ч. derived
был, так что получается. В наивной версии derived
все еще не завершен в момент создания base
с его функциями-членами, он не имеет ::dummy
вот почему это не получается)
Расширяя идею @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.
}
Там нет необходимости для traits
учебный класс. Вы можете просто использовать type_key
прямо в base
,
Вы не можете, однако, избежать передачи типа явно base
, В это время base
создается экземпляр typedef в derived
еще не видел компилятор (точнее: класс derived
еще не завершена - как это возможно, учитывая, что даже его базовый класс еще не существует).