CRTP и тип видимости - PullRequest
       4

CRTP и тип видимости

6 голосов
/ 06 декабря 2011

У меня есть эта головоломка, которую я пытаюсь решить, и она сводится к следующему примеру:

template <typename CT>
struct A
{
  typedef typename CT::VALUE_T FOO; // FOO is dependent on CT
};

template <typename CT>
struct B
{
  typedef typename CT::BAR BAR;

  BAR foo() {}
};

template <typename DT>
struct C : B<C<DT> >
{
  typedef DT VALUE_T;

  typedef typename A<C>::FOO BAR;
};

int main () {
  C<int> c;
}

Я могу попытаться объяснить вышеизложенное (я пробовал три раза и удалил текст!), Но в основном требования:

  1. C должно наследоваться от B, введенного с помощью C (с использованием CRTP), т.е. B<C<>>
  2. C - единственный, кто может создать экземпляр A (т. Е. A должен быть набран с помощью C)
  3. A является единственным, который может определить FOO (FOO зависит от типа CT, связь более сложная, чем представленная)

Проблема (как вы можете видеть из приведенного выше кода) заключается в том, что тип BAR доступен только в пределах C, и это неполно, когда создается экземпляр B, следовательно, B не видит BAR тип аргумента шаблона CT (C<int>). К сожалению, в B тип BAR используется в качестве аргументов функций и возвращаемых типов (т. Е. Не ограничивается только областью действия функции - в результате я не могу просто переместить typedef в область действия функции).

Есть ли способ обойти это? Я не могу разорвать вышеуказанные отношения (кроме как в крайнем случае). Предположительно с c ++ 11 я мог бы использовать auto и обойти необходимость иметь BAR typedef в B, однако в настоящее время это еще не вариант.

РЕДАКТИРОВАТЬ: после комментария @bitmasks, еще немного информации.

  1. Код в A и B используется в нескольких двоичных файлах в различных ситуациях, единственная уникальная ситуация в этом случае состоит в том, что C происходит от B, в других случаях C владеет экземпляром чего-то, полученного из B.
  2. Аргументы шаблона могут быть изменены (в A и B), при условии, что они могут быть установлены по умолчанию в значения, которые не требуют изменения существующих применений A и B. Тот же набор типов должен быть доступен либо в качестве параметра шаблона по умолчанию, либо в другом механизме.

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

Описание компонентов:

  • A лучше всего описать как контейнер, а FOO на самом деле является итератором, его содержимое определяется typedef параметра шаблона
  • B лучше всего описать как базовый класс, который содержит набор функций, которые вызываются некоторыми компонентами , принадлежащими экземпляру C. В предыдущих случаях этим компонентам передавалась ссылка на вещи, полученные из B (а эти вещи также принадлежат C), в данном случае я предоставляю ссылку на C сама.

Основным осложнением является доступ к контейнеру A, ранее отношения между B и C заключались в том, что C имеет экземпляр из B, но теперь C является экземпляром из B - это изменение семантики нарушает способ, которым типы вводятся в классы.

1 Ответ

4 голосов
/ 06 декабря 2011

Я думаю, что вы можете бороться с циклическим требованием typedef с параметрами шаблона по умолчанию.Следующее работает как задумано (насколько я понимаю ваш вопрос) и оставляет вам (почти) все свободы вашего исходного кода:

template <typename CT, typename CTVALUE_T = typename CT::VALUE_T>
struct A
{
    //typedef typename CT::VALUE_T FOO; // FOO is dependent on CT
    typedef CTVALUE_T FOO; // FOO is dependent on CT
};

template <typename CT, typename CTBAR = typename CT::BAR>
struct B
{
    //typedef typename CT::BAR BAR;
    typedef CTBAR BAR;

    BAR foo() {return 0;}
};

template <typename DT> struct VALUE_T__from__DT {
  typedef DT VALUE_T;
};

template <typename DT, template <class T> class _C_ > struct BAR__from__DT {
  typedef typename A<_C_<DT> , typename VALUE_T__from__DT<DT>::VALUE_T >::FOO BAR;
};

template <typename DT>
struct C : B<C<DT>, typename BAR__from__DT<DT, C >::BAR >
{
    //typedef DT VALUE_T;

    //typedef typename A<C>::FOO BAR;
};

int main () {
    C<int> c;
    int x = c.foo();
    (void)x;
    return 0;
}

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

Основная хитрость заключается в том, что определение проблемных typedef s помещается в отдельный метаконтейнер (т. е. контейнер для черт), так что вы все равно можете выполнять там любое вуду.

Я прокомментировал ненужные определения типов (но оставил их там, чтобы у вас была лучшая возможность выяснить, что я там делаю).

Подходит ли это вам?

...