Usos de un encabezado de promoción aritmética de C ++

He estado jugando con un conjunto de plantillas para determinar el tipo de promoción correcto dado dos tipos primitivos en C ++. La idea es que si define una plantilla numérica personalizada, podría usarlos para determinar el tipo de retorno de, por ejemplo, la función operador + en función de la clase transferida a las plantillas. Por ejemplo:

// Custom numeric class template  struct Complex { Complex(T real, T imag) : r(real), i(imag) {} T r, i; // Other implementation stuff }; // Generic arithmetic promotion template template  struct ArithmeticPromotion { typedef typename X type; // I realize this is incorrect, but the point is it would // figure out what X would be via trait testing, etc }; // Specialization of arithmetic promotion template template  class ArithmeticPromotion { typedef typename unsigned long long type; } // Arithmetic promotion template actually being used template  Complex<typename ArithmeticPromotion::type> operator+ (Complex& lhs, Complex& rhs) { return Complex<typename ArithmeticPromotion::type>(lhs.r + rhs.r, lhs.i + rhs.i); } 

Si usa estas plantillas de promoción, puede tratar más o menos sus tipos definidos por el usuario como si fueran primitivos con las mismas reglas de promoción que se les aplican. Entonces, supongo que la pregunta que tengo es si esto sería algo que podría ser útil? Y si es así, ¿qué tipo de tareas comunes te gustaría adaptar para facilidad de uso? Estoy trabajando en la suposición de que el solo hecho de tener las plantillas de promoción sería insuficiente para la adopción práctica.

Incidentalmente, Boost tiene algo similar en su cabecera de matemáticas / herramientas / promoción, pero realmente es más para que los valores estén listos para pasar a las funciones matemáticas estándar C (que esperan 2 o 2 dobles) y pasa por alto todos los tipos integrales. ¿Es preferible algo simple a tener un control total sobre cómo se convierten los objetos?

TL; DR: ¿Qué tipo de plantillas de ayuda esperarías encontrar en un encabezado de promoción aritmética más allá de la maquinaria que hace la promoción en sí?

Esto es definitivamente útil: usamos este tipo de cosas en la biblioteca matemática en la que trabajo para escribir correctamente los valores intermedios en las expresiones. Por ejemplo, puede tener un operador de adición con plantilla:

 template type_promote::type operator+(Atype A, Btype B); 

De esta forma, puede escribir un operador genérico que maneje diferentes tipos de argumentos, y devolverá un valor del tipo apropiado para evitar la pérdida de precisión en la expresión en la que aparece. También es útil (en cosas como sums de vectores) para declarando variables internas dentro de estos operadores.

En cuanto a la pregunta de qué debería ir con esto: acabo de verificar nuestro código fuente donde los definimos, y todo lo que tenemos allí es simplemente la statement simple de ArithmeticPromotion que describes: tres versiones genéricas para resolver el complejo complejo, complejo -real, y variantes de real-complex que usan las real-real específicas, y luego una lista de real-real – alrededor de 50 líneas de código en total. No tenemos ninguna otra plantilla de ayuda con ellos, y no parece (de nuestro uso) que haya ninguno natural que usemos.

(FWIW, si no desea escribir esto usted mismo, descargue nuestro código fuente desde http://www.codesourcery.com/vsiplplusplus/2.2/download.html , y saque src/vsip/core/promote.hpp . Eso es incluso en la parte de nuestra biblioteca que tiene licencia BSD, aunque en realidad no lo dice en el archivo).

Para esto, lo que puedes usar es el operador ?: Le dará el tipo común entre dos tipos. Primero, si los dos tipos son iguales, estás bien. Entonces, si los tipos difieren, invocará el ?: Y verá qué tipo obtiene.

Necesita un caso especial los tipos no promocionados char , short y sus versiones unsigned / signed de los mismos, ya que se aplican a dos de esos operandos de diferentes tipos, el resultado no será ninguno de ellos. También debe ocuparse del caso en que dos clases se pueden convertir a tipos aritméticos promocionados. Para hacer esto bien, verificamos si el resultado de ?: Es un tipo de aritmética promovida (en el espíritu de la cláusula 13.6 ), y luego usamos ese tipo.

 // typedef eiher to A or B, depending on what integer is passed template struct cond; #define CCASE(N, typed) \ template \ struct cond { \ typedef typed type; \ } CCASE(1, A); CCASE(2, B); CCASE(3, int); CCASE(4, unsigned int); CCASE(5, long); CCASE(6, unsigned long); CCASE(7, float); CCASE(8, double); CCASE(9, long double); #undef CCASE // for a better syntax... template struct identity { typedef T type; }; // different type => figure out common type template struct promote { private: static A a; static B b; // in case A or B is a promoted arithmetic type, the template // will make it less preferred than the nontemplates below template static identity::type &check(A, T); template static identity::type &check(B, T); // "promoted arithmetic types" static identity::type &check(int, int); static identity::type &check(unsigned int, int); static identity::type &check(long, int); static identity::type &check(unsigned long, int); static identity::type &check(float, int); static identity::type &check(double, int); static identity::type &check(long double, int); public: typedef typename cond::type type; }; // same type => finished template struct promote { typedef A type; }; 

Si sus tipos Complex se pueden convertir entre sí, ?: No encontrará un tipo común. Podrías especializarte en promote para contarle cómo descubrir un tipo común de dos Complex :

 template struct promote, Complex> { typedef Complex::type> type; }; 

El uso es simple:

 int main() { promote::type a; int *p0 = &a; promote::type b; double *p1 = &b; promote::type c; string *p2 = &c; } 

Tenga en cuenta que para los usos del mundo real, es mejor capturar algunos casos que olvidé por simplicidad, por ejemplo, debería manejarse de forma similar a (lo mejor es primero quitar la const y volatile y convertir T[N] a T* y T& a T y luego delegar en la plantilla de promote real – es decir, boost::remove_cv>::type para A y B antes de delegarlos). Si no lo hace, la llamada para check terminará en una ambigüedad para estos casos.