¿Cómo devolver el tipo correcto de datos en las plantillas?


9
#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

Aquí estoy usando plantillas en CPP, así que cuando llamo a la función sin bigpasar por argumentos doubley inttipo, quiero la respuesta de retorno que es double. El tipo aquí, devuelve en 32lugar de 32.8.

¿Cómo obtengo el resultado deseado? ¿Cómo escribir un tipo de bigfunción de retorno adecuado ?


1
Una función solo puede devolver un tipo fijo. No puede elegir en tiempo de ejecución qué tipo devolver.
Jesper Juhl

1
Es posible que desee ver cómo std::maxse implementa. El tipo de retorno de una función debe conocerse en tiempo de compilación en C ++. Por lo tanto, no puede hacer que este tipo de retorno dependa del valor de tiempo de ejecución de sus parámetros. Es por eso que para dicha función, necesita que ambos parámetros tengan el mismo tipo (es decir, que tengan el tipo X, pero no Y).
Boris Dalstein

Respuestas:


12

Una función solo puede tener un tipo de retorno que debe conocerse en tiempo de compilación. Sin embargo, puede usar std::common_type, para devolver un tipo al que ambos parámetros se pueden convertir implícitamente.

Eso sería

#include <type_traits>
template <class X, class Y>
typename std::common_type<X,Y>::type big(X a, Y b)
{
   if (a > b)
      return a;
   else return b;
}

Y para comprobar que en realidad devuelve un doublecuando se pasa un inty un doublepodemos hacer:

int main() {
    auto x = big(4.2,42);
    std::cout << std::is_same<decltype(x),double>::value;
}

Que imprime

1

PD: std::common_typepuede usar el operador ternario detrás de los escenarios y, como tal, esta solución no es muy diferente de las otras respuestas ( auto+ ternary). El verdadero poder de std::common_typees que acepta cualquier número de parámetros.


10

El tipo de retorno debe determinarse en tiempo de compilación. Puede usar el retorno final con un operador condicional , si está limitado a .

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

Ver en vivo


Sin embargo, si tiene acceso a o superior, el autoretorno es suficiente, ya que el compilador deducirá el tipo correcto si lo usa junto con el operador condicional de la siguiente manera:

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

Ver en vivo


El tipo de retorno final no es necesario, al menos a partir de C ++ 14.
Sweenish

@walnut Buen punto. Una opción más es la referencia de reenvío?
JeJo

1
@JeJo Sí, supongo que también está bien, pero probablemente no tenga sentido, porque no está modificando ninguno de los argumentos y el tipo de retorno seguirá siendo una referencia de valor en ambos casos (aunque potencialmente no const).
nogal

Eliminé mis comentarios porque ya no se aplican, pero sugeriría agregar una advertencia a la respuesta de que no puede tomar los parámetros por valor.
nogal

Si alguien mira su código, ¡parece que el parámetro pasado decidirá qué tipo de retorno obtendrá alguien, que no es el caso! Siempre obtendrá un doble, incluso si a es más grande que b.
Klaus

4

Al marcar su tipo de retorno como Yy pasar un intcomo su segundo parámetro, ha indicado claramente que Yes un int. No hay sorpresas pasando aquí.

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

Esto imprime los cuatro valores correctos en la pantalla.

https://godbolt.org/z/fyGsmo

Una cosa que es importante tener en cuenta es que esto solo funcionará para los tipos que se pueden comparar entre sí, es decir, el compilador convertirá implícitamente un tipo al otro para la comparación.

IMPORTANTE : los parámetros deben tomarse por referencia para evitar un comportamiento indefinido. Esto tiene que ver con el tipo de retorno al que terco tercamente. decltype(auto)puede devolver referencias a tipos. Si devuelve algo local a la función (recuento de argumentos), obtendrá un comportamiento indefinido.


@walnut Devolver accidentalmente una referencia es mucho más difícil de lo que parece ser este sitio. Pero es bueno saber sobre el comportamiento indefinido. No es como si fuera un código que escribiría de todos modos; Es una respuesta a una pregunta.
Sweenish

1
Ah Leí tu comentario anterior como dos puntos distintos y no como efecto y causa. Puedo hacer la edición adecuada.
Sweenish

He agregado un descargo de responsabilidad adicional.
Sweenish

2

Esta no es la solución correcta para su situación precisa, con toda probabilidad: es probable que las otras respuestas estén mucho más cerca de lo que desea.

Sin embargo, si realmente necesita devolver tipos completamente diferentes en tiempo de ejecución por alguna razón, la solución correcta (desde ) es usar a std::variant, que es una especie de unión de tipo seguro.

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

Tenga en cuenta que entonces la responsabilidad recae en la persona que llama para tratar con el valor devuelto, lo más probable es que use std::visito similar.


-2

Devuelve int porque Y es un int y le envía el 32.8. Cuando llamaste a big 32,82 es flotante, pero 8 es int y el tipo de retorno de función es Y, que también es int.

Realmente no puede solucionar esto, ya que necesita saber en tiempo de ejecución qué tipo de grandes retornos, por lo tanto, haga que ayb sean del mismo tipo:

    #include <iostream>
    using namespace std;

    template <typename X>

    X big (X a, X b)
    {
    if (a>b)
    return a;

    else return b;
    }

    int main()
    {
    cout<< big (32.8, 9.0);
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.