Generische Funktionen

Der erste Teil der Aufgabe ist relativ schlicht, solange es um einfache Datentypen geht.

// Time-stamp: "(29.11.01 18:32) loes_minmax1.cpp [Klaus Wachtler (aw38)]"
//
// erste Musterlösung für die Aufgabe, min() und max() als
// Templatefunktionen zu implementieren, mit einem kleinen
// Testprogramm für einfache Datentypen (int, double).

#include <cstdio>

using namespace std;


// die geforderten min()- und max()-Funktionen:
template<class T>
const T &min( const T &a, const T &b )
{
  return a<b ? a : b;
}

template<class T>
const T &min( const T &a, const T &b, const T &c )
{
  return min( min( a, b ), c );
}

template<class T>
const T &min( const T &a, const T &b, const T &c, const T &d )
{
  return min( min( a, b ), min( c, d ) );
}

template<class T>
const T &max( const T &a, const T &b )
{
  return a<b ? b : a;
}

template<class T>
const T &max( const T &a, const T &b, const T &c )
{
  return max( max( a, b ), c );
}

template<class T>
const T &max( const T &a, const T &b, const T &c, const T &d )
{
  return max( max( a, b ), max( c, d ) );
}


// Testprogramm dafür:
void testmin( int a, int b )
{
  printf( "min(%d,%d) = %d\n", a, b, min(a,b) );
}

void testmin( int a, int b, int c )
{
  printf( "min(%d,%d,%d) = %d\n", a, b, c, min(a,b,c) );
}

void testmin( int a, int b, int c, int d )
{
  printf( "min(%d,%d,%d,%d) = %d\n", a, b, c, d, min(a,b,c,d) );
}

void testmax( int a, int b )
{
  printf( "max(%d,%d) = %d\n", a, b, max(a,b) );
}

void testmax( int a, int b, int c )
{
  printf( "max(%d,%d,%d) = %d\n", a, b, c, max(a,b,c) );
}

void testmax( int a, int b, int c, int d )
{
  printf( "max(%d,%d,%d,%d) = %d\n", a, b, c, d, max(a,b,c,d) );
}



void testmin( double a, double b )
{
  printf( "min(%g,%g) = %g\n", a, b, min(a,b) );
}

void testmin( double a, double b, double c )
{
  printf( "min(%g,%g,%g) = %g\n", a, b, c, min(a,b,c) );
}

void testmin( double a, double b, double c, double d )
{
  printf( "min(%g,%g,%g,%g) = %g\n", a, b, c, d, min(a,b,c,d) );
}

void testmax( double a, double b )
{
  printf( "max(%g,%g) = %g\n", a, b, max(a,b) );
}

void testmax( double a, double b, double c )
{
  printf( "max(%g,%g,%g) = %g\n", a, b, c, max(a,b,c) );
}

void testmax( double a, double b, double c, double d )
{
  printf( "max(%g,%g,%g,%g) = %g\n", a, b, c, d, max(a,b,c,d) );
}

int main( int nargs, char **args )
{
  testmin( 2, 4 );
  testmin( -2, -4 );
  testmin( 20, 14 );
  testmin( 20, 14, 18 );
  testmin( 20, 18, 14 );
  testmin( 18, 20, 14, 5 );

  testmin( 2.1, 4.4 );
  testmin( -2.7, -4.6 );
  testmin( 20.1, 14.3 );
  testmin( 20.3, 14.9, 18.4 );
  testmin( 20.2, 18.1, 14.8 );
  testmin( 18.1, 20.1, 14.1, 5.0 );

  testmax( 2, 4 );
  testmax( -2, -4 );
  testmax( 20, 14 );
  testmax( 20, 14, 18 );
  testmax( 20, 18, 14 );
  testmax( 18, 14, 20, 5 );

  testmax( 2.1, 4.4 );
  testmax( -2.7, -4.6 );
  testmax( 20.1, 14.3 );
  testmax( 20.3, 14.9, 18.4 );
  testmax( 20.2, 18.1, 14.8 );
  testmax( 18.1, 20.1, 14.1, 5.0 );


  return 0;
} // main( int nargs, char **args )
Eine Variante mit variablen Parameterlisten verträgt sich leider nicht mit eigenen Klassenobjekten (und damit auch nicht mit template-Funktionen), sodaß man die Funktionen mit jeweils 2, 3, und 4 Parametern ausformulieren muß.

Für alle einfachen numerischen Datentypen ist die Vergleichsfunktion mit < definiert, so daß dafür auch eine passende min()- und max()-Funktion aus dem template generiert werden kann (siehe Funktionsschablonen (template-Funktionen)).

Problematischer wird es, wenn auch eigene Datentypen ins Spiel kommen.

In der bisherigen Form kann man das template nicht mehr verwenden, solange Objekte der eigenen Klasse nicht mit < verglichen werden können.

Als Beispiel dient eine Klasse, mit der man die wesentlichen Eigenschaften eines Studenten beschreiben kann. Zum Vergleich auf kleiner/größer soll die Intelligenz dienen.

Als Ausweg aus dem Dilemma, keinen Vergleich mit < durchführen zu können, könnte man auf die Idee kommen, eine kleine Hilfsfunktion einzuführen:

// Die Vergleichsfunktionen:
bool kleiner( const Stud &a, const Stud &b )
{
  return a.getIQ()<b.getIQ();
}

In der template-Funktion muß man dann entsprechend diese Funktion aufrufen anstatt des Operators <:

// Time-stamp: "(29.11.01 19:00) loes_minmax2.cpp [Klaus Wachtler (aw38)]"
//
// Musterlösung für die Aufgabe, min() und max() als
// Templatefunktionen zu implementieren, mit einem kleinen
// Testprogramm für eigene Datentypen.
// Um die eigenen Datentypen verwenden zu können, wird eine
// Vergleichsfunktion bool kleiner( Stud a, Stud b ) eingebaut,
// die den Vergleich liefert.

#include <iostream>
#include <string>

using namespace std;


// Die Klasse Stud:
class Stud
{
private:
  string name;
  int IQ;
  int semesteranzahl;

public:
  Stud( string name="?", int IQ = 50, int semesteranzahl = 20 )
  {
    this->name           = name;
    this->IQ             = IQ;
    this->semesteranzahl = semesteranzahl;
  }

  string getName() const
  {
    return name;
  }

  int getIQ() const
  {
    return IQ;
  }

  int getSemester() const
  {
    return semesteranzahl;
  }

};

// Die Vergleichsfunktionen:
bool kleiner( const Stud &a, const Stud &b )
{
  return a.getIQ()<b.getIQ();
}

// die geforderten min()- und max()-Funktionen:
template<class T>
const T &min( const T &a, const T &b )
{
  return kleiner(a,b) ? a : b;
}

// Versionen mit 3 und 4 Parametern wie gehabt...


template<class T>
const T &max( const T &a, const T &b )
{
  return kleiner(a,b) ? b : a;
}

// Versionen mit 3 und 4 Parametern wie gehabt...


int main( int nargs, char **args )
{
  Stud  schlau( "Hinz", 95, 3 ), doofi( "Kunz", 40, 38 );

  Stud derschlauere, derdoofe;

  derschlauere = max( schlau, doofi );
  derdoofe = min( schlau, doofi );

  cout << derschlauere.getName() << " ist schlau." << endl;
  cout << derdoofe.getName() << " nicht ganz so." << endl;

  return 0;
} // main( int nargs, char **args )

Diese Variante kommt gut mit den Objekten der Klasse Stud klar; leider nicht mehr mit einfachen Datentypen wie int, solange man nicht für jeden benutzten Datentyp eine passend überladene Funktion kleiner() zur Verfügung stellt. Das hebt leider den Nutzen einer generischen min()- oder max()-Funktion auf, wenn man dann für jeden Typ die Vergleichsfunktion liefern muß.

Eine andere Variante wäre, die ursprüngliche min()- und max()-Funktion für einfache Datentypen zu wählen, und von der template-Funktion für schwierigere Datentypen jeweils eine Spezialisierung zu schreiben:

// Time-stamp: "(29.11.01 19:17) loes_minmax3.cpp [Klaus Wachtler (aw38)]"
//
// Musterlösung für die Aufgabe, min() und max() als
// Templatefunktionen zu implementieren, mit einem kleinen
// Testprogramm für eigene Datentypen.
// Um die eigenen Datentypen verwenden zu können, wird für
// die template-Funktionen eine Spezialisierung für Stud
// geschrieben.

#include <iostream>
#include <string>

using namespace std;


// Die Klasse Stud:
class Stud
{
private:
  string name;
  int IQ;
  int semesteranzahl;

public:
  Stud( string name="?", int IQ = 50, int semesteranzahl = 20 )
  {
    this->name           = name;
    this->IQ             = IQ;
    this->semesteranzahl = semesteranzahl;
  }

  string getName() const
  {
    return name;
  }

  int getIQ() const
  {
    return IQ;
  }

  int getSemester() const
  {
    return semesteranzahl;
  }

};

// die geforderten min()- und max()-Funktionen:

// für die meisten Typen:
template<class T>
const T &min( const T &a, const T &b )
{
  return a<b ? a : b;
}

// Spezialisierung für Stud:
template<>
const Stud &min<Stud>( const Stud &a, const Stud &b )
{
  return a.getIQ()<b.getIQ() ? a : b;
}


// für alle Typen:
template<class T>
const T &min( const T &a, const T &b, const T &c )
{
  return min( min( a, b ), c );
}

template<class T>
const T &min( const T &a, const T &b, const T &c, const T &d )
{
  return min( min( a, b ), min( c, d ) );
}

// für die meisten Typen:
template<class T>
const T &max( const T &a, const T &b )
{
  return a<b ? b : a;
}

// Spezialisierung für Stud:
template<>
const Stud &max<Stud>( const Stud &a, const Stud &b )
{
  return a.getIQ()<b.getIQ() ? b : a;
}


// für alle Typen:
template<class T>
const T &max( const T &a, const T &b, const T &c )
{
  return max( max( a, b ), c );
}

template<class T>
const T &max( const T &a, const T &b, const T &c, const T &d )
{
  return max( max( a, b ), max( c, d ) );
}


int main( int nargs, char **args )
{
  Stud  schlau( "Hinz", 95, 3 ), doofi( "Kunz", 40, 38 );

  Stud derschlauere, derdoofe;

  derschlauere = max( schlau, doofi );
  derdoofe = min( schlau, doofi );

  cout << derschlauere.getName() << " ist schlau." << endl;
  cout << derdoofe.getName() << " nicht ganz so." << endl;

  return 0;
} // main( int nargs, char **args )

Jetzt kann man mit min() und max() sowohl die eingebauten Datentypen, als auch Elemente von Stud verwenden.

Am elegantesten ist allerdings die Lösung, bei der für die Klasse Stud definiert wird, was als kleiner anzusehen ist. Dazu überlädt man einfach den Operator < (siehe dazu auch Überladen von Operatoren), indem man in der Klasse eine Funktion operator<() definiert.

Durch diese Vorgehensweise kann man min() und max() (ebenso wie alle anderen template-Funktionen, die den Operator < voraussetzen) ohne Spezialisierung aufrufen:

// Time-stamp: "(29.11.01 19:26) loes_minmax4.cpp [Klaus Wachtler (aw38)]"
//
// Musterlösung für die Aufgabe, min() und max() als
// Templatefunktionen zu implementieren, mit einem kleinen
// Testprogramm für eigene Datentypen.
// Um die eigenen Datentypen verwenden zu können, wird für
// Stud der Operator < überschrieben.

#include <iostream>
#include <string>

using namespace std;


// Die Klasse Stud:
class Stud
{
private:
  string name;
  int IQ;
  int semesteranzahl;

public:
  Stud( string name="?", int IQ = 50, int semesteranzahl = 20 )
  {
    this->name           = name;
    this->IQ             = IQ;
    this->semesteranzahl = semesteranzahl;
  }

  string getName() const
  {
    return name;
  }

  int getIQ() const
  {
    return IQ;
  }

  int getSemester() const
  {
    return semesteranzahl;
  }

  bool operator<( const Stud &rechteSeite ) const
  {
    return this->IQ<rechteSeite.IQ;
  }

};

// die geforderten min()- und max()-Funktionen:

// für alle Typen, die den Operator < kennen:
template<class T>
const T &min( const T &a, const T &b )
{
  return a<b ? a : b;
}

// für alle Typen:
template<class T>
const T &min( const T &a, const T &b, const T &c )
{
  return min( min( a, b ), c );
}

template<class T>
const T &min( const T &a, const T &b, const T &c, const T &d )
{
  return min( min( a, b ), min( c, d ) );
}

// für alle Typen, die den Operator < kennen:
template<class T>
const T &max( const T &a, const T &b )
{
  return a<b ? b : a;
}

// für alle Typen:
template<class T>
const T &max( const T &a, const T &b, const T &c )
{
  return max( max( a, b ), c );
}

template<class T>
const T &max( const T &a, const T &b, const T &c, const T &d )
{
  return max( max( a, b ), max( c, d ) );
}


int main( int nargs, char **args )
{
  Stud  schlau( "Hinz", 95, 3 ), doofi( "Kunz", 40, 38 );

  Stud derschlauere, derdoofe;

  derschlauere = max( schlau, doofi );
  derdoofe = min( schlau, doofi );

  cout << derschlauere.getName() << " ist schlau." << endl;
  cout << derdoofe.getName() << " nicht ganz so." << endl;

  return 0;
} // main( int nargs, char **args )

AnyWare@Wachtler.de