Especialização C ++, tipo_of ou apenas tipoid
-
20-09-2019 - |
Pergunta
Eu gostaria de saber o que é melhor usar na minha situação e por quê. Primeiro de tudo, ouvi dizer que o uso do RTTI (tipoID) é ruim. Alguém poderia explicar por quê? Se eu sei exatamente tipos, o que há de errado em compará -los em tempo de execução? Além disso, há algum exemplo de como usar o boost :: type_of? Eu não encontrei nenhum pesquisando no poderoso Google :) Outra solução para mim é a especialização, mas eu teria especializar pelo menos 9 tipos de novos métodos. Aqui está um exemplo do que eu preciso:
Eu tenho esta aula
template<typename A, typename B, typename C>
class CFoo
{
void foo()
{
// Some chunk of code depends on old A type
}
}
Então, eu preciso verificar o tipo (o que eu ouvi é ruim) e fazer essas três realizações em exemplo como:
void foo()
{
if (typeid(A) == typeid(CSomeClass)
// Do this chunk of code related to A type
else
if (typeid(B) == typeid(CSomeClass)
// Do this chunk of code related to B type
else
if (typeid(C) == typeid(CSomeClass)
// Do this chunk of code related to C type
}
Então, qual é a melhor solução? Não quero me especializar para todos os A, B, C, porque todo tipo tem 3 especializações, então obterei 9 métodos ou apenas essa verificação do tipo.
Solução
É ruim porque
- A, B e C são conhecidos em tempo de compilação, mas você está usando um mecanismo de tempo de execução. Se você invocar TypeId, o compilador garantirá metadados nos arquivos de objeto.
- Se você substituir "faça esse pedaço de código relacionado a um tipo" por código real que utiliza a interface do CSomeclass, verá que não poderá compilar o código no caso A! = CSomeclass e ter uma interface incompatível. O compilador ainda tenta traduzir o código, mesmo que nunca seja executado. (Veja o exemplo abaixo)
O que você normalmente faz é levar em consideração o código em modelos de função separados ou funções estáticas de membros de classes que podem ser especializadas.
Mau:
template<typename T>
void foo(T x) {
if (typeid(T)==typeid(int*)) {
*x = 23; // instantiation error: an int can't be dereferenced
} else {
cout << "haha\n";
}
}
int main() {
foo(42); // T=int --> instantiation error
}
Melhor:
template<typename T>
void foo(T x) {
cout << "haha\n";
}
void foo(int* x) {
*x = 23;
}
int main() {
foo(42); // fine, invokes foo<int>(int)
}
Saúde, s
Outras dicas
Bem, geralmente as soluções podem surgir sem RTTI. Ele "pode" mostrar que você não pensou no design do software corretamente. Isso é mau. Às vezes rtti posso Seja uma coisa boa.
Não há algo estranho no que você quer fazer. Você não poderia criar um modelo intermediário projetado algo como a seguir:
template< class T > class TypeWrapper
{
T t;
public:
void DoSomething()
{
}
};
Em seguida, se especialize parcialmente pelas funções que você deseja da seguinte maneira:
template<> class TypeWrapper< CSomeClass >
{
CSomeClass c;
public:
void DoSomething()
{
c.DoThatThing();
}
};
Então, em sua classe, defina acima, você faria algo como ...
modelo
class CFoo
{
TypeWrapper< A > a;
TypeWrapper< B > b;
TypeWrapper< C > c;
void foo()
{
a.DoSomething();
b.DoSomething();
c.DoSomething();
}
}
Dessa forma, ele realmente faz algo na chamada "doSomething" se estiver passando pelo modelo parcialmente especializado.
O problema está nos pedaços de código que você escreve para cada especialização.
Não importa se você escreve (longamente)
void foo()
{
if (typeid(A) == typeid(CSomeClass)
// Do this chunk of code related to A type
else
if (typeid(B) == typeid(CSomeClass)
// Do this chunk of code related to B type
else
if (typeid(C) == typeid(CSomeClass)
// Do this chunk of code related to C type
}
ou
void foo()
{
A x;
foo_( x );
B y;
foo_( y );
C z;
foo_( z );
}
void foo_( CSomeClass1& ) {}
void foo_( CSomeClass2& ) {}
void foo_( CSomeClass3& ) {}
A vantagem do segundo caso é que, quando você adiciona uma classe D, você é lembrado pelo compilador de que há uma sobrecarga para que você tenha falta o que você deve escrever. Isso pode ser esquecido na primeira variante.
Receio que isso não funcione em primeiro lugar. Esses "pedaços de código" precisam ser compiláveis, mesmo que o tipo não seja csomeclass.
Eu não acho que o tipo_of também ajudará (se for o mesmo que o Auto e o dtype em C ++ 0x).
Eu acho que você poderia extrair esses três pedaços para funções separadas e sobrecarregar cada uma para o csomeclass. (Editar: Oh, existem else if's
. Então você pode realmente precisar de muitas sobrecargas/especialização. Para que serve esse código?)
Edit2: parece que seu código espera fazer o equivalente ao seguinte, onde int é o tipo especial:
#include <iostream>
template <class T>
bool one() {return false; }
template <>
bool one<int>() { std::cout << "one\n"; return true; }
template <class T>
bool two() {return false; }
template <>
bool two<int>() { std::cout << "two\n"; return true; }
template <class T>
bool three() {return false; }
template <>
bool three<int>() { std::cout << "three\n"; return true; }
template <class A, class B, class C>
struct X
{
void foo()
{
one<A>() || two<B>() || three<C>();
}
};
int main()
{
X<int, double, int>().foo(); //one
X<double, int, int>().foo(); //two
X<double, double, double>().foo(); //...
X<double, double, int>().foo(); //three
}
Eu acho que você entendeu suas abstrações em algum lugar.
Eu tentaria redefinir A, B&C em termos de interfaces que eles precisam expor (classes base abstratas em C ++ com métodos virtuais puros).
O modelo permite basicamente o pato, mas parece que o CFOO sabe muito sobre as aulas da AB&C.
typeId é ruim porque:
- O TypeID pode ser caro, binários inchados, carrega informações extras que não devem ser necessárias.
- Nem todos os compiladores suportam
- Está basicamente quebrando a hierarquia da classe.
O que eu recomendaria é refatorar: Remova o modelo, em vez disso, defina interfaces para A, B&C e faça o CFOO levar essas interfaces. Isso o forçará a refatorar o comportamento para que os B&C sejam na verdade tipos coesos.