¿Cuando usar decltype y auto?

publicado por: Anonymous

Buenas, sé que ambos realizan la deducción de tipos, pero ¿Cual es la diferencia entre ambos? ¿Cuándo es mas conveniente usar decltype sobre auto y viceversa?

solución

auto permite deducir el tipo a partir del valor con el que se inicia la variable:

auto i = 5;   // int
auto f = 4.5; // double

Sin embargo auto no es capaz de cubrir todo el abanico de posibilidades…

Imagínate que tienes que programar una plantilla tal que:

tipo1 func(objeto)
{ return objeto.otraFunc(); }

¿Cómo programas esa plantilla?

versión 1

auto func(auto objeto)
{
  return objeto.otraFunc();
}

Válido en C++17, no así en C++11

versión 2

template<class T, class U>
T func(U objeto)
{ return objeto.otraFunc(); }

Compatible con todas las versiones pero un poco engorroso de usar, ya que si bien el tipo U se puede deducir automáticamente el tipo T habrá que indicarlo de forma explícita:

struct POO
{
  int otraFunc()
  { return 5; }
};

POO MiObjeto;
std::cout << func<int>(MiObjeto);

versión 2.1

Con el fin de deducir los dos valores creamos una estructura intermedia a modo de traits:

struct POO
{
  int otraFunc()
  { return 5; }
};

// Implementación por defecto = no valida
template<class T>
struct traits;

template<>
struct traits<POO>
{
  typedef int returnType;
};

template<class T>
typename traits<T>::returnType func(T objeto)
{ return objeto.otraFunc(); }


int main()
{
  POO MiObjeto;
  std::cout << func(MiObjeto);
}

Aunque es facil entender que esta solución empieza a ser bastante engorrosa.

versión 3

… o también podemos usar decltype

struct POO
{
  int otraFunc()
  { return 5; }
};

template<class T>
decltype(T().otraFunc()) func(T objeto)
{ return objeto.otraFunc(); }

    int main()
{
  POO MiObjeto;
  std::cout << func(MiObjeto);
}

En este caso, decltype evalúa la expresión T().otraFunc() que no es más que:

  • T(): Simula la creación de un nuevo objeto de tipo T
  • T().otraFunc() simula la llamada y se queda con el tipo de retorno de la función otraFunc para el tipo T

Podría suceder que el constructor por defecto T no estuviese disponible para un tipo dado… en cuyo caso la expresión anterior no podría ser evaluada… Podemos obtener una versión más genérica usando punteros:

template<class T>
decltype(((T*)nullptr)->otraFunc()) func(T objeto)
{ return objeto.otraFunc(); }

Como decltype no ejecuta código sino que simplemente lo evalúa no hay riesgo de fugas de memoria ni de llamadas sobre objetos no válidos… claro que tampoco podemos usar new

Por cierto, hay gente que prefiere esta otra nomenclatura totalmente válida en C++11

template<class T>
auto func(T objeto) -> decltype(((T*)nullptr)->otraFunc())
{ return objeto.otraFunc(); }

Por supuesto decltype también se puede utilizar para evaluar expresiones más sencillas:

decltype(5) var = 4; // decltype(5) == int
std::cout << var;    // imprime 4

float foo();
decltype(foo()) var2 = 4.5// float

decltype(var2) var3 = 3; // float

EDITO

Otra forma de usar decltype, en combinación con declval. Así no tenemos que preocuparnos por cómo se debe crear el objeto T:

template<class T>
auto func(T objeto) -> decltype(std::declval<T&>().otraFunc())
{ return objeto.otraFunc(); }
Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *