C++: O esquecido operador vírgula :::: Rodrigo Strauss :: www.1bit.com.br
C++: O esquecido operador vírgula
Um operador que é pouco usado, mas bastante útil em C++, é o operador vírgula. Resumidamente, ele avalia todas as instruções separadas pela vírgula, mas retorna sempre a última. Olhe um exemplo:
a = (b,c);
Nesse caso, o valor da variável a será 3.
Uma das particularidades do operador vírgula, é que ele tem a menor precedência entre todos os operadores. Vamos tirar os parênteses:
a = b,c;
Agora o valor da variável a será 2, já que pela precedência, a = b é a primeira expressão, e c é a segunda expressão.
Usos realmente úteis
O uso mais comum para o operador vírgula é inicializar mais de uma variável em um loop, como no exemplo abaixo:
for(int a = 10, b = 20, c = 30 ; a < 10 ; a += --b, c++)
Nesse caso, nós declaramos a variável a e atribuímos o valor 20 à variável b.
Outro ótimo uso do operador vírgula é para fazer validação de casts não seguros. Existem lugares onde, apesar do parâmetro ser do tipo void* ou void**, você sempre deve passar um tipo determinado. Um exemplo clássico, é a função CoCreateInstance, que tem a seguinte assinatura:
O último parâmetro, apesar de ser LPVOID* (ou seja, void**), espera um tipo definido. Ele quer um ponteiro para um ponteiro que possa ser convertido para IUnknown*.
Olhe o código a seguir, que tem um dos erros mais comuns em programação COM:
hr = CoCreateInstance(CLSID_BLA,
(void**)pUnk);
Encontrou o erro? Na realidade, &pUnk deveria ser passado, e não somente pUnk como foi passado. Com isso você vai ganhar um belo GPF de presente. Como estamos usando cast C, o compilador não reclama, e nosso código é compilado sem problemas. E aqui não podemos usar static_cast, porque IUnknown** não pode ser convertido para void**.
Vamos agora a uma solução usando o operador vírgula e uma macro:
// IC = interface cast
#define IC(ppUnk) (static_cast(*ppUnk),(void**)ppUnk)
IC(&pUnk));
O que fizemos aqui foi o seguinte: a macro primeiro checa se o apontado do que foi passado pode ser convertido para IUnknown*, usando static_cast. Essa primeira expressão (static_cast(*ppUnk)) vai verificar a validade do ponteiro em tempo de compilação, mas não surtirá nenhum efeito em tempo de execução. A segunda expressão ((void**)ppUnk) fará então o cast que deve ser feito para a chamada da função. Vamos agora tentar compilar com o mesmo erro do primeiro exemplo:
IC(pUnk));
error C2440: 'static_cast' : cannot convert from 'IUnknown' to 'IUnknown *'
No user-defined-conversion operator available
that can perform this conversion, or the operator
E como todo ponteiro para interface COM pode ser convertido para IUnknown*, resolvemos nosso problema.
Se você quiser mais informações sobre verificações em tempo de compilação e códigos nesse estilo, procure sobre metaprogramming e estude muito, mas muito mesmo sobre templates.