No he visto tu código original, pero seguramente, el ejemplo que has pillado sería equivalente al siguiente:
const wchar_t* wstr = /* lo que sea */; // Cadena const.
unsigned char* ustr = reinterpret_cast<unsigned char*>(const_cast<wchar_t*>(wstr));
Sin embargo, tú tendrás lo siguiente:
wchar_t* wstr = /* lo que sea */; // Cadena no-const.
unsigned char* ustr = reinterpret_cast<unsigned char*>(const_cast<wchar_t*>(wstr));
El error está en el `const_cast`. El `const_cast` sirve para quitar/poner el calificativo `const` a un puntero o una referencia. Ejemplos:
int a = 3;
// Ok, const_cast recibe un puntero (int*) y lo convierto a puntero-a-constante (const int*).
const int* ptr_a = const_cast<const int*>(&a);
const int* ptr2_a = &a; // Ok, la conversion a const tambien funciona de forma implicita.
// Aqui el `const_cast` es absolutamente necesario. Quitarle `const` a un puntero/referencia nunca
// es implicito (ponerselo si), y hay que forzarlo con `const_cast`.
int* ptr3_a = const_cast<int*>(ptr_a);
// Este es el error de tu codigo! `ptr3_a` no es constante, y el tipo de salida del casting, tampoco (int*),
// pero `const_cast` requiere que uno de los dos sea constante y el otro no! Si los dos son iguales
// te da un error de compilacion.
int* ptr4_a = const_cast<int*>(ptr3_a);
No he puesto ejemplos con referencias porque no es tu caso. ¿Cuál es tu problema? Que tu `const_cast<wchar_t*>(wstr)` está diciendo: "quítale el `const` a `wstr`", pero `wstr` resulta que no es `const`, así que no hay nada que quitar, y de ahí el error de compilación. De todas formas, utilizar un `const_cast` es peligroso, y podría provocar incluso violaciones de segmento (especialmente con cadenas). Si algo se marco como `const`, es por algo.
Bueno, continuemos. ¿Y qué es lo que hace `reinterpret_cast<tipo>(var)`? Pues sencillamente lo siguiente: coje la dirección de memoria de la variable que le has pasado, y a partir de ahí, coje `sizeof(tipo)` bytes y lo devuelve como un objeto de tipo `tipo`. Ajin! Completamente a pelo, y éso es muy peligroso.
Por ejemplo (relacionandolo con tu caso):
wchar_t wc = /* un caracter, el que sea */
unsigned char c = reinterpret_cast<unsigned char>(wc);
Veamos el peligro: al menos en mi arquitectura, `sizeof(wc) == 4`, mientras que `sizeof(char) == 1`. Al realizar dicho `reinterpret_cast`, el compilador coje el primer byte the `wc` y devuelve la secuencia de bits como un `unsigned char`, sin adaptar la representación ni nada, y `c` tendría una copia del primer byte the `wc`.
¿Qué pasa con cadenas/punteros?
wchar_t* wc = /* lo que sea. Cadena de 5 caracteres. */
unsigned char* c = reinterpret_cast<unsigned char*>(wc);
Aquí `wc` es un puntero (y todos los punteros tienen el mismo tamaño en una misma arquitectura), así que `reinterpret_cast` devuelve la misma dirección de memoria, pero ahora desde `c` estás manipulando `unsigned char`s y no `wchar_t`s. Así que:
for (unsigned i = 0; i < 5; ++i)
cout << c[i] << endl;
Aquí, tu crees que estás imprimiendo los 5 primeros carácteres de la cadena original (transformada como `unsigned char`), pero resulta que estás imprimiendo ¡los 4 primeros bytes del primer carácter de la cadena `wchar_t` original, más el primer byte de la segunda! Habría que hacerlo de la siguiente forma:
for (unsigned i = 0; i < 5 * sizeof(wchar_t); ++i)
cout << c[i] << endl;
Además, utilizarlo de la siguiente forma tiene muchos riesgos, el más importante es que `wchar_t` es un tipo muy especial, cuya implementación es a gusto del compilador, así que en teoría, no hay forma de saber cómo está representado cada carácter. ¿Están los bytes puestos de abajo a arriba? ¿De arriba a bajo? ¿Con complemento a uno o a dos? ¿Cada carácter tiene 2 bytes, 4 u 8?
Así que, si trabajar con `reinterpret_cast` es peligroso de por sí (solo debe usarse en situaciones muy controladas), y utilizarlo para llegar de `wchar_t` a `char` lo es más aún.
Siempre que puedas, utiliza `char*`, a no ser que sea exclusivamente necesario.