c++ templates c++11 variadic-functions decltype

c++ - tipo de retorno final usando decltype con una función de plantilla variadica



templates c++11 (5)

Aparentemente no puedes usar decltype de forma recursiva (al menos por el momento, tal vez lo arreglen)

Puede usar una estructura de plantilla para determinar el tipo de la suma

Se ve feo pero funciona

#include <iostream> using namespace std; template<typename... T> struct TypeOfSum; template<typename T> struct TypeOfSum<T> { typedef T type; }; template<typename T, typename... P> struct TypeOfSum<T,P...> { typedef decltype(T() + typename TypeOfSum<P...>::type()) type; }; template <class T> T sum(const T& in) { return in; } template <class T, class... P> typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p) { return t + sum(p...); } int main() { cout << sum(5, 10.0, 22.2) << endl; }

Quiero escribir un sumador simple (para risitas) que suma todos los argumentos y devuelve una suma con el tipo apropiado. Actualmente, tengo esto:

#include <iostream> using namespace std; template <class T> T sum(const T& in) { return in; } template <class T, class... P> auto sum(const T& t, const P&... p) -> decltype(t + sum(p...)) { return t + sum(p...); } int main() { cout << sum(5, 10.0, 22.2) << endl; }

En GCC 4.5.1, esto parece funcionar bien para 2 argumentos, por ejemplo, sum (2, 5.5) regresa con 7.5. Sin embargo, con más argumentos que esto, recibo errores de que sum () simplemente no está definido aún. Si declaro sum () como este sin embargo:

template <class T, class P...> T sum(const T& t, const P&... p);

Entonces funciona para cualquier cantidad de argumentos, pero sum (2, 5.5) devolvería el entero 7, que no es lo que yo esperaría. Con más de dos argumentos supongo que decltype () tendría que hacer algún tipo de recursión para poder deducir el tipo de t + suma (p ...). ¿Es esto legal C ++ 0x? o ¿decltype () solo funciona con declaraciones no variadicas? Si ese es el caso, ¿cómo escribirías esa función?


Creo que el problema es que la plantilla de función variadic solo se considera declarada después de haber especificado su tipo de devolución, de modo que esa sum en decltype no puede referirse nunca a la plantilla de función variadic. Pero no estoy seguro de si esto es un error de GCC o C ++ 0x simplemente no lo permite. Supongo que C ++ 0x no permite una llamada "recursiva" en la parte ->decltype(expr) .

Como solución, podemos evitar esta llamada "recursiva" en ->decltype(expr) con una clase de rasgos personalizada:

#include <iostream> #include <type_traits> using namespace std; template<class T> typename std::add_rvalue_reference<T>::type val(); template<class T> struct id{typedef T type;}; template<class T, class... P> struct sum_type; template<class T> struct sum_type<T> : id<T> {}; template<class T, class U, class... P> struct sum_type<T,U,P...> : sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};

De esta forma, podemos reemplazar el tipo de decltype en su programa con typename sum_type<T,P...>::type y compilará.

Editar: Dado que esto realmente devuelve decltype((a+b)+c) lugar de decltype(a+(b+c)) que estaría más cerca de cómo se usa la suma, podría reemplazar la última especialización con esto:

template<class T, class U, class... P> struct sum_type<T,U,P...> : id<decltype( val<T>() + val<typename sum_type<U,P...>::type>() )>{};


La solución de C ++ 14:

template <class T, class... P> auto sum(const T& t, const P&... p){ return t + sum(p...); }

El tipo de devolución se deduce automáticamente.

Véalo en el compilador en línea


Otra respuesta a la última pregunta con menos tipeo al usar std::common_type C ++ 11: Simplemente use

std::common_type<T, P ...>::type

como tipo de devolución de su suma variable.

En cuanto a std::common_type , aquí hay un extracto de http://en.cppreference.com/w/cpp/types/common_type :

Para tipos aritméticos, el tipo común también se puede ver como el tipo de expresión aritmética (posiblemente modo mixto) como T0 () + T1 () + ... + Tn ().

Pero obviamente esto funciona solo para expresiones aritméticas y no cura el problema general.


Proporciono esta mejora a la respuesta aceptada. Solo dos estructuras

#include <utility> template <typename P, typename... Ps> struct sum_type { using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>()); }; template <typename P> struct sum_type<P> { using type = P; };

Ahora solo declara tus funciones como

template <class T> auto sum(const T& in) -> T { return in; } template <class P, class ...Ps> auto sum(const P& t, const Ps&... ps) -> typename sum_type<P, Ps...>::type { return t + sum(ps...); }

Con esto, tu código de prueba ahora funciona

std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl;

146.5