C ++ 0x: técnica de plantilla variádica
-
27-10-2019 - |
Pregunta
Me estoy preparando para la definición de literales definidos por el usuario con un Plantilla variádica
template<...>
unsigned operator "" _binary();
unsigned thirteen = 1101_binary;
GCC 4.7.0 no admite operator ""
Sin embargo, pero puedo simular esto con una función simple hasta entonces.
Por desgracia, mi recursión es la forma incorrecta. No puedo pensar en una buena manera de cómo No cambie los valores más a la derecha, sino la izquierda más:
template<char C> int _bin();
template<> int _bin<'1'>() { return 1; }
template<> int _bin<'0'>() { return 0; }
template<char C, char D, char... ES>
int _bin() {
return _bin<C>() | _bin<D,ES...>() << 1; // <-- WRONG!
}
Lo que por supuesto no está del todo bien:
int val13 = _bin<'1','1','0','1'>(); // <-- gives 10
Porque mi recursión cambia la derecha más alejada, y no las de la izquierda.
Probablemente sea algo pequeño, pero no puedo verlo.
- ¿Puedo corregir la línea?
_bin<C>() | _bin<D,ES...>() << 1;
? - ¿O tengo que reenviarlo todo y darle vueltas todo después (no es agradable)?
- ¿O alguna otra forma en que no pueda ver?
Actualizar: No pude doblar la recursión al revés, pero descubrí sizeof...
. Funciona, pero no perfecto. ¿Hay otra manera?
template<char C, char D, char... ES>
int _bin() {
return _bin<C>() << (sizeof...(ES)+1) | _bin<D,ES...>() ;
}
Solución
En cualquier paso de la recursión, ya conoces el rango del dígito más izquierdo.
template<char C> int _bin();
template<> int _bin<'1'>() { return 1; }
template<> int _bin<'0'>() { return 0; }
template<char C, char D, char... ES>
int _bin() {
return _bin<C>() << (1 + sizeof...(ES)) | _bin<D,ES...>();
}
Otros consejos
Los paquetes de parámetros son relativamente inflexibles, y generalmente no escribe algoritmos directamente en ellos. Las plantillas de funciones variádicas son buenas para el reenvío, pero lo empacaría en un más manejable tuple
antes de tratar de manipularlo.
Usando un simple binary_string_value
metafunción donde el lugar de 1 viene primero y un genérico tuple_reverse
metafunción, el patrón sería
template< char ... digit_pack >
constexpr unsigned long long _bin() {
typedef std::tuple< std::integral_constant< digit_pack - '0' > ... > digit_tuple;
return binary_string_value< typename tuple_reverse< digit_tuple >::type >::value;
}
Una posibilidad sería usar un acumulador:
template <char C>
int _binchar();
template<>
int _binchar<'0'>() { return 0; }
template<>
int _binchar<'1'>() { return 1; }
template<char C>
int _bin(int acc=0) {
return (acc*2 + _binchar<C>());
}
template<char C, char D, char... ES>
int _bin(int acc=0) {
return _bin<D, ES...>(acc*2 + _binchar<C>());
}