1
#include <iostream>
#include <map>

using namespace std;

template< class K, class Y > class Contactos{
  K tipo;
  Y contacto;

public: 
  Conctacos( ){ };
  Contactos( K t, Y c ){ this->tipo = t; this->contacto = c; }
  void Modificar( Y c ){ this->contacto = c; }
  K gettipo( ){ return this->tipo; }  
  Y getcont( ){ return this->contacto; }
};

class Persona{
  int dni;
  string nombre, apellido;

public:
  Persona( );
  Persona( int a, string b, string c ) { this->dni = a; this->nombre = b; this->apellido = c; }
  int getdni( ){ return dni; }
  string getnombre( ){ return nombre; }
  string getap( ){ return apellido; }
};

int main( int argc, char *argv[] ) {
  multimap< Persona, Contactos > agenda;

  Persona p1( 41228981, "Alejandro", "Perez" );
  Contactos c1( "Whatsapp", 3441212 );

  agenda.insert( make_pair( p1, c1 ) );

  return 0;
}
Trauma
  • 25,297
  • 4
  • 37
  • 60
ale
  • 11
  • 1
  • Hola y bienvenido a [es.so]. Nótese que esta pregunta tiene problemas de formato. Dale a [edit](/edit) para modificarla siguiendo las reglas de lo que es aceptado en este sitio: [¿Qué tipo de preguntas puedo hacer aquí?](/help/on-topic). También puede serte de interés realizar el [tour] y leer [ask]. – José Manuel Ramos Oct 27 '18 at 13:47
  • Te falta la especialización de la plantilla `Contactos` en la declaración del objeto `c1`, y lo mismo con la especialización de la plantilla `Contactos` para el `multimap agenda` – José Manuel Ramos Oct 27 '18 at 13:48

1 Answers1

0

Tienes una serie de fallos que hacen que tu código no compile:

  • El nombre del constructor por defecto de Contactos está mal escrito, no es Conctacos si no Contactos.
  • La clase Contactos es una clase plantilla, pero se te olvida mencionarlo en varios sitios.
  • Ni la clase Contactos ni la clase Persona dispone de comparador (operator <) por lo que no pueden formar parte de un multimapa.

También tienes otros fallos que aunque no hacen que tu programa no compile, hacen que tu código sea más propenso a errores:

  • Nomenclatura.
    • Los nombres de tus variables o tipos no sou autoexplicativos, nombres como K, Y, a, b o c no explican nada del objetivo ni función a la que sirven; los nombres deberían permitir saber de un vistazo su cometido, haciendo que el código sea menos propenso a errores y haciendo que sea más fácil de leer y entender, cosa que cualquier persona que trabaje contigo (incluso tu yo futuro) agradecerá mucho.
    • El plural debería ser usado para colecciones, no para datos individuales. Tu clase Contactos almacena un solo contacto, no tiene sentido que sea plural.
  • Estilo.
    • Usar this para referirte a las variables miembro de una clase es un engorro; ciertamente puede ayudar a la legibilidad en funciones grandes, pero en funciones pequeñas su uso es más molesto que práctico.
    • Preguntate si tiene sentido que algunas clases tengan constructor por defecto. Tanto Contactos como Persona pueden ser construidas sin datos pero seguramente no tiene sentido que exista una instancia sin datos. Pero en caso de que tenga sentido, no dejes las variables miembro sin inicializar, es responsabilidad del constructor por defecto el dar valor a las variables miembro cuando no se les asigna valor en la construcción del objeto.
    • No hay obligación de usar la cláusula using namespace std; pues es sólo es una ayuda a la escritura de código; si decides usar esta cláusula no lo hagas en el ámbito global, úsala en el ámbito más pequeño posible. Lee este hilo para saber por qué.
    • Deja a tus código respirar, añade unos espacios y saltos de línea. En los 90 teníamos pantallas de 80 caracteres de ancho y 25 líneas de alto, por entonces tenía sentido apretujar el código para que cupiera en la pantalla. Hoy día tenemos pantallas enormes y separar las cosas hace que el código sea más legible.
  • Estás usando la clase std::string en Persona pero no has incluido la cabecera <string>. Seguramente dicha cabecera se ha incluido a través de <iostream>, pero es una buena práctica añadir todas las cabeceras que uses directamente pues no hay garantía de que siembre se incluyan indirectamente las que necesitas.

Teniendo en cuenta los puntos anteriores, comentaré las correcciones que me parecen adecuadas para tu código:

Propuesta.

La clase Contactos debería ser Contacto:

template< class tipo_t, class contacto_t > class Contacto {
  tipo_t m_tipo{};
  contacto_t m_contacto{};

public: 
  Contacto( ) = default;

  Contacto( tipo_t a_tipo, contacto_t a_contacto ) :
      m_tipo{a_tipo},
      m_contacto{a_contacto}
  {}

  void Modificar( contacto_t a_contacto ) {
      m_contacto = a_contacto;
  }
  tipo_t &gettipo( ) {
      return m_tipo;
  }
  contacto_t &getcontacto( ) {
      return m_contacto;
  }
  const tipo_t &gettipo( ) const {
      return m_tipo;
  }
  const contacto_t &getcontacto( ) const {
      return m_contacto;
  }
};

Adicionalmente, he renombrado los tipos K y T a tipo_t y contacto_t pues esos nombres ayudan a saber qué son los tipos de la plantilla, el sufijo _t nos indica que son un tipo (no un valor ni una variable) y es un sufijo utilizado comunmente para tipos en C++ incluso por la STL.

Las variables miembro han recibido un trato parecido, al añadirles el prefijo m_ estamos indicando que son miembro, de manera que cuando al verlas en el código, sabremos de un vistazo que pertenecen a un objeto, esto es una variante de la notación húngara. En su definición les he añadido unas llaves al final ({}) para invocar su constructor por defecto, los tipos tipo_t y contacto_t deberán tener constructor por defecto para que esto funcione. Adicionalmente los argumentos de las funciones les he añadido el prefijo a_ para indicar que son argumentos y he eliminado el this en el cuerpo de la función.

El constructor por defecto lo he marcado con = default para que el compilador lo genere por su cuenta, dado que m_tipo y m_contacto están siendo inicializadas en el punto de definición, lo que hará el compilador será darles el valor por defecto de su constructor por defecto. En el constructor con parámetros he dejado el cuerpo vacío y movido la inicialización de las variables miembro a la lista de inicialización .

Finalmente, he hecho que las funciones get devuelvan el objeto por referencia y les he añadido la variante constante para cuando Contacto sea uasdo en un entorno de solo lectura. Esto permite hacer contacto.getcontacto() = 100 para modificar las variables miembro. He renombrado getcont a getcontacto por claridad, ya que cont no ayuda a entender qué estamos obteniendo (¿qué es cont? ¿contador, contacto, contrato, concupiscencia?) por otro lado, me disgusta mucho mezclar idiomas get es inglés y contacto, tipo, etc... es Español, prefiero que sea homogéneo.

Modifico la clase Persona con los mismos criterios:

class Persona{
  int m_dni{};
  std::string m_nombre{}, m_apellido{};

public:
  Persona( ) = default;

  Persona( int a_dni, std::string a_nombre, std::string a_apellido ) :
      m_dni{a_dni},
      m_nombre{a_nombre},
      m_apellido{a_apellido}
  {}

  int &getdni( ) {
      return m_dni;
  }
  std::string &getnombre( ) {
      return m_nombre;
  }
  std::string &getapellido( ) {
      return m_apellido;
  }
  const int &getdni( ) const {
      return m_dni;
  }
  const std::string &getnombre( ) const {
      return m_nombre;
  }
  const std::string &getapellido( ) const {
      return m_apellido;
  }
};

Finalmente llegamos a la parte importante:

int main() {
  using namespace std;

  multimap< Persona, Contacto<std::string, int> > agenda;
  //                          ~~~~~~~~~~~, ~~~ <--- Especificar argumentos plantilla

  Persona p1( 41228981, "Alejandro", "Perez" );
  Contacto<std::string, int> c1( "Whatsapp", 3441212 );
  //       ~~~~~~~~~~~, ~~~ <--- Especificar argumentos plantilla    
  agenda.insert( make_pair( p1, c1 ) );

  return 0;
}

Con esas correcciones el código aún no te funcionará, porque el multimapa no puede usar Persona como clave porque la clase carece de comparador, puedes añadir un comparador en cualquier punto que sea visible a la instancia de multimapa:

bool operator<(const Persona &a, const Persona &b) {
    return a.getdni() < b.getdni();
}

Con este criterio ordenaría por DNI, para ordenar por nomre usarías evidentemente el nombre. Y para ordenar por ambos usa std::tie :

bool operator<(const Persona &a, const Persona &b) {
    return std::tie(a.getdni(), a.getnombre()) < std::tie(b.getdni(), b.getnombre());
}

Aunque lo que tiene sentido es ordenar por apellido y nombre (ya que el DNI no se puede repetir):

bool operator<(const Persona &a, const Persona &b) {
    return std::tie(a.getapellido(), a.getnombre()) < std::tie(b.getapellido(), b.getnombre());
}

Podrías omitir los argumentos de plantilla al crear una instancia de Contacto en main, pero con los parámetros que usas, se deduciría que tipo_t sería const char *, para evitar este problema podemos añadir una guía de deducción al constructor con parámetros de Contacto:

template< class tipo_t > Contacto(const char *, tipo_t) -> Contacto<std::string, tipo_t>;

Con este cambio, evitas tener que especificar los argumentos de plantilla al crear la instancia de Contacto y aún así se deducirá el tipo correcto.

Puedes ver el código completo, con mis propuestas, en Wandbox.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82