2013-03-09 2 views
1

По практическим причинам, у меня есть класс, какНаследование и CRTP

template <class A> 
class CRTP 
{ 
    template <int (A::*Member)()> 
    int func(void * obj) 
    { 
     int result 
     // Do something with Member, like 
     // result = (reinterpret_cast<A*>(obj)->*Member)(); 
     return result; 
    } 
}; 

template <void (A::*Member)()> является требование, оно не может быть передан в качестве аргумента.

И у меня есть базовый класс

class Base : public CRTP<Base> 
{ 
    int aMemberOfBase() {...} 
}; 

и ее производная, которые я хочу также наследовать CRTP

class Derived : public CRTP<Derived>, public Base 
{ 
    int aMemberOfDerived() {...} 
}; 

В некотором члене производные, я сделаю что-то вроде

func<&Derived::aMemberOfDerived>(this); 
func<&Base::aMemberOfBase>(this); 

Возможно ли это?

(Ну, VC++ скомпилировать только первую строку и не хочет читать о втором ... но производные должен иметь член

template <int (Base::*Member)()> int func(void * obj); 
template <int (Derived::*Member)()> int func(void * obj); 

который выглядит странно, я признаю это. Но следующий фрагмент кода

template <void (Base::*Member)()> int func() {return 0;} 
template <void (Derived::*Member)()> int func() {return 1;} 

компилирует и вернуть func<&Base::someMember>() != func<&Derived::someMember>(), потому что подпись шаблона не то же самое, и не может быть такой же.)

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

Кроме того, если я объявляю

class Derived : public Base, public CRTP<Derived> 

вместо

class Derived : public CRTP<Derived>, public Base 

Я получаю компиляции ошибки времени (на всех func<...>(...)), что означает, что есть что-то не так-то.

С другой стороны, я знаю, что

template <class A, int (A::*Member)()> int func(void * obj) 

устранило бы необходимость в CRTP, но болезненные писать func<Derived, &Derived::aMember>(). Существует ли такое решение, как

template <class Class, void (Class::*Member)()> class A 
{ 
    void func(void * obj) {...(reinterpret_cast<Class*>(obj)->*Member)();...} 
}; 
template <typename Signature> class B; 

template <typename Class, typename Member> 
class B<&Class::Member> : public class A<Class, &Class::Member> {}; 

которое позволило бы B<&Base::Derived>().func(somePtrToBase)?

+0

Ну, после прочтения несвязанных статей, кажется, что Дон Клугстон в своем «самом быстром делетете» столкнулся с теми же проблемами. Поэтому я решил отказаться от CRTP и повторить аргументы. Спасибо за вашу помощь. – Fred

ответ

1

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

template <typename A> struct CRTP 
{ 
    template <int (A::*Member)()> int func(); 
}; 

struct Base : CRTP<Base> 
{ 
    int aMemberOfBase(); 
}; 

struct Derived : CRTP<Derived>, Base 
{ 
    int aMemberOfDerived(); 

    void foo() 
    { 
     CRTP<Derived>::func<&Derived::aMemberOfDerived>(); 
     CRTP<Base>::func<&Base::aMemberOfBase>(); 
    } 
}; 

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

+0

Да. Но я не хочу повторять. Мой вопрос: «Могу ли я избежать этого повторения? Если да, то как. Если нет, то почему?" – Fred

+0

Ой, кстати, я попытался использовать общедоступное/частное наследование (' class Base: private CRTP '), чтобы отключить использование' Base :: fun <...> (...) 'в классе Derived, так что couln 't быть какой-то двусмысленностью, но 'class Derived: public Base, private CRTP ' не работает. – Fred

+1

@Fred: проверка доступа выполняется * после * разрешения перегрузки, а не раньше. Игра с 'private' и' public' не будет помогать. –

Смежные вопросы