Considere esta plantilla:

template<typename t>
t add(t a, t b) {
    return a+b;
}

¿Cómo puedo pasar diferentes tipos de parámetros para que el valor de retorno sea:

  • int si ambos parámetros son del tipo int.

  • float si uno de los parámetros es del tipo float.

  • float si ambos parámetros son del tipo float.

También he intentado tener múltiples parámetros para la plantilla:

template<typename t, typename c>

Utilizándolos para los parámetros de función para que puedan ser diferentes (t add(t a, c b)) pero lo que no puedo entender es cómo puedo cambiar el tipo de función (int, float, double, etc.) dependiendo de tipo de retorno?

12
Dumi B 25 dic. 2016 a las 13:10

3 respuestas

La mejor respuesta

Lo que quiere es std::common_type:

template<typename T0, typename T1>
typename std::common_type<T0, T1>::type add(T0 a, T1 b) {
    return a+b;
}

La documentación dice:

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

Pero, como lo señaló @ Jarod42 en los comentarios, esto es solo una vista, y puede ser incorrecto en algunos casos: por ejemplo, std::common_type<char, char>::type es char mientras que la expresión aritmética char() + char() produce { {X3}}.


Una implementación más completa puede emitir explícitamente el resultado para eliminar posibles advertencias en los casos citados anteriormente:

template<typename T0, typename T1, typename R = std::common_type_t<T0, T1>>
R add(T0 a, T1 b) {
    return static_cast<R>(a+b);
}

Aquí std::common_type se usa por defecto para el tipo de retorno, pero dado que es un parámetro de plantilla, puede especificar un tipo diferente al usar la función (puede ser útil en casos de uso más complejos):

char a = 1, b = 2;
add<decltype(a), decltype(b), int>(a, b);

Utilizando std::conditional y std::is_same, una solución aún más completa propuesta por @ Jarod42 en los comentarios permite tener la plantilla {{X2 }} como primer parámetro, y mantenga la deducción automática para a y b:

template <typename R, typename T0, typename T1>
using ResType = std::conditional_t<
    std::is_same<void, R>::value,
    std::common_type_t<T0, T1>, // default
    R                           // R was explicitly specified: use it
>;

template <typename R = void, typename T0, typename T1>
ResType<R, T0, T1> add(T0 a, T1 b)
{
    return static_cast<ResType<R, T0, T1>>(a + b);
}

Uso:

char a = 1, b = 2;
add(a, b);       // returns char
add<int>(a, b);  // returns int
12
wasthishelpful 25 dic. 2016 a las 11:24

En c ++ 11 y versiones posteriores, puede usar std::common_type

template<typename T1, typename T2>
auto add(T1 a, T2 b) -> typename std::common_type<T1, T2>::type {
  return a + b;
}

Funcionará como si estuvieran sucediendo promociones integrales, no muy diferentes a solo hacer una deducción de tipo auto, como en Piotr . Pero realmente comenzará a brillar si intenta pasar tipos personalizados como parámetros, como las clases std::complex.


Prefiero preservar los tipos de promoción integral que impone el estándar, por lo que quizás esto debería envolverse en otro rasgo.

template<typename T1, typename T2>
class AdditionTraits {
  using common_t = typename std::common_type<T1, T2>::type;
  constexpr static bool is_char = std::is_same<common_t, char>::value;
public:
  using type = typename std::conditional<is_char, int, common_t>::type;
};

template<typename T1, typename T2>
auto add(T1 a, T2 b) -> typename AdditionTraits <T1, T2>::type {
  return a + b;
}

Usted puede ver que agrega char s en un int , como lo hacen las reglas de promoción estándar.

6
Community 23 may. 2017 a las 12:01

Use auto deducción de tipo (desde c ++ 14):

template<typename t, typename u>
    auto add(t a, u b) {
        return a+b;
    }
11
Green Tree 25 dic. 2016 a las 11:04