Usando o static cast
De ccppbrasil.org
Usando o static_cast
O operador static_cast faz a conversão entre tipos relacionados, por exemplo de um ponteiro para outro, faz a conversão de uma enumeração para um tipo integral ou de um tipo de ponto flutuante para um tipo integral. Vamos examinar cada caso:
1) Conversão entre ponteiros/referências
class base {};
class derived : public base {};
void f() {
derived d;
base *ptr_base = static_cast<base*>(&d);
//...
}
É possível colocar o static_cast para fazer a conversão de um ponteiro para da classe derivada para a classe base. No entanto não existe nenhum problema em se deixar o código como segue abaixo:
... derived d; base *ptr_base = &d; ...
Pois o compilador irá fazer a mesma checagem.
2) Conversão entre enumerações e tipo integrais
Exemplo
enum Enum { a, b, c , d = 2147483647 };
...
int i = a;
...
Lembrando que uma enumeração é um tipo integral de um tamanho que caiba seu maior elemento. A conversão de "Enum" para um tipo integral é aceitavél naturalmente sendo o "Enum" um tamanho menor ou igual ao tipo integral. É preciso tomar cuidado em uma situação como a seguinte:
... char c = d; ...
Pois o enum pode estar contendo um valor maior que o tamanho do char (Este tipo de verificação poderia ser feita em tempo de compilação com asserts estáticos) Se você tem certeza que o valor cabe em um char, você pode usar um static_cast para dizer ao compilador: "eu sei o que estou fazendo".
char c = static_cast<char>(d);
Conversão de um tipo integral para um enum. Esta conversão é naturalmente errada, pois não existe garantia que o tipo integral contenha um valor válido no enum. Neste caso é preciso colocar um static_cast pois o compilador nem sequer aceita a conversão Evite este tipo de construção. Se for preciso, coloque uma comentário com a justificativa.
int i = 2; Enum e = i; // não compila Enum e = static_cast<Enum>( i ); // ok, eu sei que o valor 2 é valido para o enum porque ...
3) Conversão entre tipos de ponto flutuante e tipos integrais
Exemplo:
int i = 3; double d = i;
Esta conversão é natural e não apresenta grandes problemas. É possivel utilizar um static_cast mas ele não traz muitos benefícios.
Segundo exemplo:
double d = 3.4; int i = static_cast<int>(d);
Nesta conversão ocorre com trucamento. Não é obrigatório o uso do static_cast. Mas é recomendado pois fica mais claro que o programador tem conciência que vai haver truncamento no cast.
Para casts numéricos com verificação de intervalos veja: http://www.boost.org/libs/numeric/conversion/doc/numeric_cast.html
Agora examinando melhor o caso dos ponteiros.
O static_cast também pode ser usado aonde seja clara que a conversão entre ponteiros pode ser feita.
Por exemplo:
void f(int i)
{
void * ptr = &i;
f2(static_cast<int*>(ptr));// declarada como: void f2(int * p);
}
Se a mesma função fosse definida assim:
void f(void *ptr) {
f2(static_cast<int*>(ptr));// declarada como: void f2(int * p);
}
Não existe nenhuma garantia na conversão acima. Este tipo de construção deve ser evitada, mas se por algum motivo seja necessária então neste caso é melhor utilizar um reinterpret_cast. O static_cast também pode ser usado, mas por uma questão de clareza é interessante colocar um reinterpret_cast. Não vou detalhar o reinterpret_cast aqui, mas basicamente o reinterpret_cast é usado em último caso forçando uma conversão que o programador deve garantir. Eu reitero, que este tipo de contrução pode e deve ser evitada.
void f(void *ptr) {
//OBS: eu tenho que fazer este cast porque....
f2(reinterpret_cast<int*>(ptr));// declarada como: void f2(int * p);
}
Convertento ponteiros da classe base para a classe derivada
void () {
derived d;
base * pb = &d;
derived * pd = static_cast<derived*> (pb); // ok, neste contexto esta garantido
}
Agora outro exemplo
void (base * base) {
derived * pd = static_cast<derived*>(base);
}
Neste exemplo não existe garantia nenhuma que o cast é possível. Não é recomendado usar o static_cast nesta situação. Este tipo de código deve ser evitado. Para este tipo de cast é recomendado o dynamic_cast. Se por algum motivo este código fosse usado ele deveria ter um comentário justificando. Abaixo um exemplo válido usando dynamic_cast
void (base * base) {
derived * pd = dynamic_cast<derived*>(base);
if (pd != 0)
{
// base precisa ser um tipo polimórfico
// ok, cast válido
}
}
Usando static_cast para remover ambiguidades
Veja o exemplo abaixo:
class A {};
class B : public A {};
class C : public A {};
class D : public C, public B {};
...
D d;
A * pA = static_cast<B*>(&d);
...
A classe D pode ser convertida na classe A usando a herança da classe B ou da classe C. Neste caso é obrigatório usar o cast para remover a ambiguidade.
Resumindo (conselhos)
[1] Use static_cast sempre que fizer uma conversão de um tipo integral para um enum. Se a validade da conversão não estiver clara coloque uma justificativa.
[2] Use static_cast para reforçar a idéia que um valor do enum cabe em um tipo integral que pode ser menor que o enum. Caso a conversão seja clara não coloque o static_cast.
[3] Use static_cast para converter um tipo de ponto flutuante para um tipo integral. Isso deixa claro que o valor pode ser truncado e que você tem conciência disto.
[4] Use static_cast para fazer casts de ponteiros relacionados dentro de um contexto que fique garantida a conversão.
[5] Não use static_cast para fazer conversão de um ponteiro de uma classe derivada para uma classe base. Não é necessário a não ser que exista alguma ambiguidade.
[6] Só use static_cast para converter um ponteiro da classe base para a classe derivada em um contexto em que a conversão esteja garantida. Se você não puder garantir altere sua função. Caso você precise deste comportamento utilize um dynamic_cast. Em último caso coloque uma boa justificafica da garantia do cast.
[7] Evite o uso do reinterpret_cast
[8] Não use o cast estilo C no C++.
[9] Lembre-se que muitos casts em seu programa pode indicar um design incorreto.
Referências
[Stroustrup, 2000] B. Stroustrup: “The C++ Programming Language (Special Edition)”. Addison Wesley. Reading Mass. USA. February 2000. ISBN 0-201-70073-5.
