Подкласс CRTP и список экземпляров - PullRequest
1 голос
/ 11 июня 2011

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

Моя главная цель - создать единый способ доступа к списку экземпляров каждого подкласса.

Возможно, проблема заключается в использовании пространства имен.

Вот код самой простой версии: http://ideone.com/rFab5

Моя настоящая проблема больше похожа на: http://ideone.com/U7cAf

У меня есть дополнительное предупреждение с использованием clang ++:

test.cpp:28:63: warning: static data member specialization of 'instances' must originally be declared in namespace 'NS1'; accepted as a C++0x extension [-Wc++0x-extensions]
template <> std::list<NS1::Derived*> NS1::Base<NS1::Derived>::instances;
                                                          ^ 
test.cpp:15:34: note: explicitly specialized declaration is here
        static std::list<T*> instances;

Проблема была обновлена, поскольку она не ведет себя так же, используя пространства имен.

Проблема отредактирована для отправки кода на Ideone

Ответы [ 4 ]

1 голос
/ 11 июня 2011

Проблема в том, что вы пытались определить переменную списка неправильно. В общем, вам нужно предоставить определение для Base - вы не просто определяете его для одной части, которая оказывается подклассом Derived, если это не явная специализация.

template<typename T> std::list<T*> NS1::Base<T>::instances;

http://ideone.com/Vclac

Компилируется без ошибок. Нет промежуточных звеньев или чего-либо подобного.

0 голосов
/ 11 июня 2011

Afaik, у вас есть следующие опции.
Во-первых, если Intermediate всегда шаблонизируется для производного типа, вам не нужен список для него, потому что он никогда не будет самым производным типом.Если он может быть основан на шаблонах других типов / не может быть получен, вы можете добавить параметр шаблона по умолчанию для нетипичного bool, например, так:

template<bool, class A, class B>
struct select_base{
  typedef A type;
};

template<class A, class B>
struct select_base<false,A,B>{
  typedef B type;
};

template<class T, bool IsDerived = false>
class Intermediate
  : public select_base<IsDerived,
                       Base<T>,
                       Base<Intermediate<T> >
                       >::type
{
  // ...
};

// derived use
class Derived : public Intermediate<Derived, true>
{
  // ...
};

// non-derived use:
Intermediate<int> im;

Если промежуточный класс не является шаблоном и не является производным от Base, вам нужно снова получить из Base в наиболее производном классе:

class Derived : public Intermediate, public Base<Derived>
{
  // ...
};

Большая проблема возникает, когда промежуточное звено также происходит от Base, но не является шаблонным.Вы можете добавить производный тип по умолчанию, но это сделает использование без производных более уродливым:

#include <type_traits> // C++0x, use std::
//#include <tr1/type_traits> // C++03, use std::tr1::

struct nil_base{};

template<class Derived = nil_base>
class Intermediate
  : public select_base<std::is_same<Derived,nil_base>::value,
                       Base<Intermediate<Derived> >, //
                       Base<Derived>
                       >::type
{
  // ...
};

// derived use now without boolean flag
class Derived : public Intermediate<Derived>
{
  // ...
};

// non-derived use a bit uglier
Intermediate<> im;
//          ^^ -- sadly needed
0 голосов
/ 11 июня 2011

Следующая компиляция в порядке с MinGW g ++ 4.4.1, MSVC 10.0 и Comeau Online 4.3.10.1:

#include <list>

template <class T>
class Base
{
protected:
    Base()
    {
        instances.push_back(static_cast<T*>(this));
    }
private:
    static std::list<T*> instances;
};

template <class U>
class Intermediary : public Base<U>
{
protected:
    Intermediary()
    :Base<U>()
    {
    }
};

class Derived : public Intermediary<Derived>
{
public:
    Derived()
    :Intermediary<Derived>()
    {
    }
};

template<class Derived> std::list<Derived*> Base<Derived>::instances;

int main()
{}

Определение instances дословно скопировано из вашего вопроса.* Я говорю как Исаак Ньютон, я не выдвигаю никаких гипотез!

Приветствия & hth.,

0 голосов
/ 11 июня 2011

При изменении Base() и Intermediary() на Base<U>() и Intermediary<Derived> в конструкторах получается код OK для GCC.

Нет смысла менять определение instances во втором случае: шаблон идентичен первой ситуации.

...