STL basisklassen



  • Hallo miteinander,

    ich hab gerade einen Hänger bezüglich der STL-Basisklassen wie z. B. unary_function oder binary_function. Im Buch "The C++ Programming Language" und in der MSDN wird empfohlen functoren (function objects) von solchen Basisklassen abzuleiten um die dort festgelegten, einheitlichen Begriffe zu verwenden.

    So weit, so einleuchtend. Jetzt bin ich aber über die Definitionen einiger STL-Funktionen gestolpert. Z. B. ist logical_not aus dem <functional>-Header dort folgender maßen definiert:

    template<class T>
    struct logical_not
        : public unary_function<T, bool>
    {
        bool operator ()(const T& x) const
        {
            return !x;
        }
    };
    

    Unterläuft diese Definition nicht den Grundgedanken der Basisklassen, und müsste obige Definition nicht richtiger wie folgt lauten?

    template<class T>
    struct logical_not
        : public unary_function<T, bool>
    {
        result_type operator ()(const argument_type& x) const
        {
            return !x;
        }
    };
    

    Wo liegt sonst der Vorteil der Basisklassen. Was habe ich evtl. übersehen?

    Bin für jeden Hinweis Dankbar.
    Viele Grüße
    Knecht



  • Knecht schrieb:

    und müsste obige Definition nicht richtiger wie folgt lauten?

    Da hast Du im Prinzip recht -- aber versuch das mal zu kompilieren. Das klappt nicht, Du musst die Typen vollständig qualifizieren -- und das zerstört den Code noch viel effektiver:

    template<class T>
    struct logical_not : public unary_function<T, bool>
    {
        typename unary_function<T, bool>::result_type operator ()(const typename unary_function<T, bool>::argument_type& x) const
        {
            return !x;
        }
    };
    


  • Danke für die Antwort. Aber ich bin da noch nicht ganz dabei. Die Definition für unary_function sieht in meiner functional.h (Visual Studio 2005 Standard) so aus:

    template<class _Arg, class _Result>
    struct unary_function
    {	// base class for unary functions
        typedef _Arg argument_type;
        typedef _Result result_type;
    };
    

    Das heißt doch dass die typedefs public sind, oder? Und da ich diese Basisklasse public vererbe müsste ich diese ohne vollständige qualifizierer verwenden können.

    Mein VS compiliert obiges Beisiel jedenfalls anstandslos.

    Nochmals Danke.
    Viele Grüße
    Knecht



  • Knecht schrieb:

    Mein VS compiliert obiges Beisiel jedenfalls anstandslos.

    Hmm, es ist möglich, dass der GCC da streikt. Ich habe leider gerade auch den Standard nicht zur Hand. Prinzipiell müsstest Du eigentlich recht haben.



  • Knecht schrieb:

    Unterläuft diese Definition nicht den Grundgedanken der Basisklassen, und müsste obige Definition nicht richtiger wie folgt lauten?

    nö. du weißt doch, daß da bool steht.
    das erben hat eher den zweck, daß jeder fremde dich nach den üblichen sachen fragen kann. sonst würde man meistens gar keinen result_type anbieten zum beispiel. besser ist, man machts gleich mit.



  • Knecht schrieb:

    Das heißt doch dass die typedefs public sind, oder? Und da ich diese Basisklasse public vererbe müsste ich diese ohne vollständige qualifizierer verwenden können.

    Moooment. Dass die Typedefs public sind, bedeutet, dass du überhaupt auf sie zugreifen kannst. Du musst den Aufruf allerdings qualifizieren, weil dem Compiler bei der Schablonenerstellung keine konkrete Klasse als Basisklasse vorliegt und er somit nicht erkennen kann, dass es sich um einen Typ handelt, der dazu auch noch in der Basisklasse versteckt ist.



  • volkard schrieb:

    ... das erben hat eher den zweck, daß jeder fremde dich nach den üblichen sachen fragen kann. sonst würde man meistens gar keinen result_type anbieten zum beispiel. besser ist, man machts gleich mit.

    Wenn ich das richtig verstanden habe heisst das also, dass die Vererbung nur für den Zugriff von "außen" benötigt wird, da die außenstehenden methoden ja u. U. nicht wissen welche Typen verwendet wurden. Das macht Sinn.

    [Edit:] @Konrad Rudolph & Michael E.: Mit den Qualifizierungen hab Ihr schon recht. Hab beim Ausprobieren die unary_function-Struktur einer nicht-Vorlagenklasse vererbt. Dass es dort funktioniert ist Klar. Sorry dafür!!!

    Herzlichen Dank für alle Antworten und einen schönen Abend. 🙂 👍

    Viele Grüße
    Knecht



  • Hmmm,

    ich hatte das so verstanden, dass die Ursache für das "typedef wird nicht gefunden"-Problem daran liegt, dass man nicht einfach von unary_function erbt, sondern damit ein template erstellt.
    Leitet man "konkret" ab

    struct logical_not_int
        : public unary_function<int, bool>
    {
        result_type operator ()(const int& x) const
        {
            return !x;
        }
    };
    

    funktioniert's nämlich.

    Ich bin kein template-Profi, aber nach meiner Erinnerung kümmert sich der Compiler "erst spät um Vererbung". Will sagen: Unqualifizierte Namen in templates werden innerhalb des templates aufzulösen versucht - NICHT in Basisklassen; weil er (hier) erst das logical_not-template ganz "verstanden haben" muss, bevor er weiß, auf welche konkrete Basisklasse er zugreifen soll. Bei voll qualifizierten Namen, hat man dem Compiler bereits mitgeteilt, wo er suchen soll/kann.

    Hat man aber kein template, sondern eine konkrete Klasse, fällt dieser Schritt weg.

    Stimmt's so in etwa ? Habe ich vielleicht dasselbe gesagt/gemeint, wie alle anderen ? ...
    EDIT: Vielleicht wusste das der Knecht auch schon ... und ich habe sein Post erst jetzt verstanden ....

    Gruß,

    Simon2.



  • Jo, stimmt im Groben.

    Deutlich machen kann man sich das an diesem Beispiel:

    template<int>
    struct Base
    {
        typedef int foo;
    };
    
    template< >
    struct Base<0>
    {
        void foo();
    };
    
    template<int n>
    class Derived : Base<n>
    {
        typedef typename Base<n>::foo;    // erzeugt Fehler für n = 0
    };
    


  • Danke,

    Simon2.


Anmelden zum Antworten