2015-05-14 2 views
6

У меня есть следующий базовый шаблонный класс.Как принудительно использовать любопытно повторяющийся шаблон шаблона в C++

template<typename T> 
class Base { 
    public: 
    void do_something() { 
    } 
}; 

Он предназначен для использования в качестве с любопытством повторяющегося узора шаблона. Он должен быть унаследован как class B : public Base<B>. Он должен не унаследован как class B : public Base<SomeoneElse>. Я хочу статически применять это требование. Если кто-то использует это неправильно, я ожидаю ошибку на этапе компиляции.

Что я делаю, это положить static_cast<T const&>(*this) в do_something(). Таким образом, класс, наследующий шаблон, наследуется или наследуется от класса, предоставленного в качестве параметра шаблона. Извините за запутанное выражение. На простом английском языке он требует, чтобы B был или наследуется от SomeoneElse в class B : public Base<SomeoneElse>.

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

Однако я хочу сделать больше. Я хочу, чтобы B был SomeoneElse сам. Как я могу это сделать?

+0

'static_cast' ничего не обеспечивает; это просто вызывает неопределенное поведение, если '* this' на самом деле не' T'. –

+0

@ T.C. Он выполняет статические проверки. Тот, о котором вы говорите, должен быть 'reinterpret_cast'. –

+0

Он проверяет только, что 'T' происходит от' Base ', а не' * this' является 'T'. т. е. задано 'class Bar: public Base {}; класс Foo: public Base {}; ', ваш чек ничего не поймает. –

ответ

8

Сделать конструктор (или деструктор) Base приватным, а затем сделать T a friend. Таким образом, единственное, что может построить/уничтожить Base<T>, - это T.

+0

См. Http://stackoverflow.com/questions/702650/making-a-template-parameter-a-friend. Кажется, вы не можете сопоставить параметр шаблона. –

+0

@Hot.PxL. Голосовой ответ в этом вопросе объясняет, как это сделать в конце ('friend T;', а не 'friend class T;'). –

+0

Вы правы. Но есть ли способ сделать это, когда C++ 11 недоступен? –

3

Если ваш класс содержит код, который говорит:

T* pT = 0; 
Base *pB = pT; 

Тогда будет ошибка компиляции, если T не уступка совместим с Base.

Этот вид проверки оформляется в C++ 11, так что вам не придется писать его вручную и может получить полезные сообщения об ошибках:

#include <type_traits> 

template<typename T> 
class Base { 

public: 
    void do_something() 
    { 
     static_assert(
      std::is_base_of<Base, T>::value, 
      "T must be derived from Base"); 
    } 
}; 

class B : public Base<B> { }; 

int main() 
{ 
    B b; 
    b.do_something(); 
} 

Как обеспечить, чтобы параметр типа Base «s является именно тот класс, который вытекает из него, который кажется концептуально ошибочным. Класс, действующий как базовый класс, не может «говорить» о том, какой тип наследует его. Он может наследоваться несколько раз через множественное наследование или вообще не наследоваться.

+0

Я предполагаю, что он удовлетворяет только одной стороне уравнения. Я также хочу убедиться, что 'B' - это точно' SomeoneElse'. –

+0

Это не во всех случаях на clang3.6, он жалуется, что производный класс является неполным во время static_assert. –

+0

@RichardHodges Может зависеть от того, где вы ставите 'static_assert'. Я добавил полный пример, который кажется прекрасным на MSVC 2013. Попробует его на последнем clang, когда я получу шанс. –

1

Два хороших ответа до сих пор. Вот еще один, который использует идиому генерации пользовательских ключей доступа к определенным методам (в данном случае конструктору). Он обеспечивает абсолютную гарантию правильного использования, не подвергая частные методы в базе производным.

Он также может использоваться для управления доступом к другим методам базового класса в каждом конкретном случае.

template<class Derived> 
struct Base 
{ 
private: 
    // make constructor private 
    Base() = default; 
protected: 
    // This key is protected - so visible only to derived classes 
    class creation_key{ 
     // declare as friend to the derived class 
     friend Derived; 
     // make constructor private - only the Derived may create a key 
     creation_key() = default; 
    }; 

    // allow derived class to construct me with a key 
    Base(creation_key) 
    {} 

    // other methods available to the derived class go here 

private: 
    // the rest of this class is private, even to the derived class 
    // (good encapsulation) 
}; 

struct D1 : Base<D1> 
{ 
    // provide the key 
    D1() 
    : Base<D1>(creation_key()) 
    {} 

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