En ISO C99 / C11, el tipo de juego de palabras basado en uniones es legal, por lo que puede usarlo en lugar de indexar punteros a no matrices (consulte varias otras respuestas).
ISO C ++ no permite juegos de palabras basados en uniones. GNU C ++ lo hace, como una extensión , y creo que algunos otros compiladores que no son compatibles con las extensiones GNU en general sí admiten el juego de palabras de tipo union. Pero eso no le ayuda a escribir código estrictamente portátil.
Con las versiones actuales de gcc y clang, escribir una función miembro de C ++ usando a switch(idx)
para seleccionar un miembro se optimizará para índices constantes en tiempo de compilación, pero producirá un asm terrible y ramificado para índices en tiempo de ejecución. No hay nada intrínsecamente malo switch()
en esto; esto es simplemente un error de optimización perdido en los compiladores actuales. Podrían compilar la función switch () de Slava de manera eficiente.
La solución / solución alternativa a esto es hacerlo de la otra manera: dé a su clase / estructura un miembro de matriz y escriba funciones de acceso para adjuntar nombres a elementos específicos.
struct array_data
{
int arr[3];
int &operator[]( unsigned idx ) {
// assert(idx <= 2);
//idx = (idx > 2) ? 2 : idx;
return arr[idx];
}
int &a(){ return arr[0]; } // TODO: const versions
int &b(){ return arr[1]; }
int &c(){ return arr[2]; }
};
Podemos echar un vistazo a la salida de asm para diferentes casos de uso, en el explorador del compilador Godbolt . Estas son funciones completas de System V x86-64, con la instrucción RET final omitida para mostrar mejor lo que obtendría cuando estén en línea. ARM / MIPS / lo que sea similar.
# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
mov eax, DWORD PTR [rdi+4]
void setc(array_data &d, int val) { d.c() = val; }
mov DWORD PTR [rdi+8], esi
int getidx(array_data &d, int idx) { return d[idx]; }
mov esi, esi # zero-extend to 64-bit
mov eax, DWORD PTR [rdi+rsi*4]
En comparación, la respuesta de @ Slava usando a switch()
para C ++ hace que asm sea así para un índice de variable de tiempo de ejecución. (Código en el enlace Godbolt anterior).
int cpp(data *d, int idx) {
return (*d)[idx];
}
# gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
# avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
cmp esi, 1
je .L6
cmp esi, 2
je .L7
mov eax, DWORD PTR [rdi]
ret
.L6:
mov eax, DWORD PTR [rdi+4]
ret
.L7:
mov eax, DWORD PTR [rdi+8]
ret
Esto es obviamente terrible, en comparación con la versión de juego de palabras basada en la unión de C (o GNU C ++):
c(type_t*, int):
movsx rsi, esi # sign-extend this time, since I didn't change idx to unsigned here
mov eax, DWORD PTR [rdi+rsi*4]