Restrições de Modelo C ++

Em C #, podemos definir um tipo genérico que impõe restrições aos tipos que podem ser usados ​​como o parâmetro genérico. O exemplo a seguir ilustra o uso de restrições genéricas:

interface IFoo { } class Foo where T : IFoo { } class Bar : IFoo { } class Simpson { } class Program { static void Main(string[] args) { Foo a = new Foo(); Foo b = new Foo(); // error CS0309 } } 

Existe uma maneira que podemos impor restrições para parâmetros de modelo em C ++.


C ++ 0x tem suporte nativo para isso, mas eu estou falando sobre o padrão atual C ++.

Como alguém mencionou, o C ++ 0x está incorporando isso ao idioma. Até então, eu recomendaria as sugestões de Bjarne Stroustrup para restrições de modelo .

Edit: Boost também tem uma alternativa própria .

Edit2: Parece que os conceitos foram removidos do C ++ 0x .

Se você usar C ++ 11, você pode usar static_assert com std::is_base_of para essa finalidade.

Por exemplo,

 #include  template class YourClass { YourClass() { // Compile-time check static_assert(std::is_base_of::value, "type parameter of this class must derive from BaseClass"); // ... } } 

“Implicitamente” é a resposta correta. Os modelos criam efetivamente um cenário de “tipagem de pato”, devido à maneira como são compilados. Você pode chamar qualquer function que desejar em um valor tipado por modelo, e as únicas instanciações que serão aceitas são aquelas para as quais esse método é definido. Por exemplo:

 template  int compute_length(T *value) { return value->length(); } 

Podemos chamar esse método em um ponteiro para qualquer tipo que declare o método length() para retornar um int . Assim:

 string s = "test"; vector vec; int i = 0; compute_length(&s); compute_length(&vec); 

… mas não em um ponteiro para um tipo que não declara length() :

 compute_length(&i); 

Este terceiro exemplo não irá compilar.

Isso funciona porque o C ++ compila uma nova versão da function de modelo (ou class) para cada instanciação. Ao realizar essa compilation, ela faz uma substituição direta, quase macro, da instanciação de modelo no código antes da verificação de tipos. Se tudo ainda funcionar com esse modelo, a compilation continuará e eventualmente chegaremos a um resultado. Se alguma coisa falhar (como int* não declarando length() ), então obtemos o temido erro de tempo de compilation do modelo de seis páginas.

Você pode colocar um tipo de guarda no IFoo que não faz nada, certifique-se de que está lá em T no Foo:

 class IFoo { public: typedef int IsDerivedFromIFoo; }; template  class Foo { typedef typename T::IsDerivedFromIFoo IFooGuard; } 

Confira o Boost

A biblioteca de verificação de conceito de reforço (BCCL)

A biblioteca de verificação de conceito permite adicionar uma declaração explícita e verificação de conceitos no estilo da extensão de linguagem C ++ proposta .

Tipo de. Se você está static_cast para um IFoo *, então será impossível instanciar o template a menos que o chamador passe uma class que pode ser atribuída a um IFoo *.

Apenas implicitamente.
Qualquer método que você usa em um método que é realmente chamado é imposto no parâmetro do modelo.

Você consegue. Crie o modelo base. Faça com que tenha apenas construtores privados. Em seguida, crie especializações para cada caso que você deseja permitir (ou faça o oposto se a lista não permitida for muito menor que a lista permitida).

O compilador não permitirá que você instancie os modelos que usam a versão com construtores privados.

Este exemplo só permite instanciação com int e float.

 template class FOO { private: FOO(){}}; template<> class FOO{public: FOO(){}}; template<> class FOO{public: FOO(){}}; 

Não é uma maneira curta e elegante de fazê-lo, mas é possível.

Veja o padrão CRTP (Curiously Recursive Template Pattern). Ele é projetado para ajudar a suportar a inheritance estática.