Const Correctness



  • Hallo Forum,

    in OpenCV gibt es die Klasse cv::Mat, die unter anderem ein Bild repräsentieren kann. Copy-Constructor und Zuweisungsoperator der Klasse sind so gestaltet, dass der eigentliche Bildinhalt nicht kopiert wird sondern nur die übrigen Member.

    cv::Mat foo(cv::imread(R"(C:\myImg.png)"); // Bild laden
    cv::Mat bar(foo); // "Kopie" erzeugen
    cv::circle(bar, cv::Point(30, 30), 5, CV_RGB(255, 0, 0)); // Kreis in bar zeichnen -> ändert (auch) foo
    

    Nehmen wir an, ich habe eine Klasse, die ein cv::Mat Member hat und die ich mit einem entsprechenden Getter ausrüsten möchte:

    struct Foo
    {
       Foo(const std::string& imgPath) :
          m_img(cv::imread(imgPath))
       {}
    
       const cv::Mat& GetMat() const { return m_img; } // wie sollte die Signatur dieses Getters aussehen?
       
    private:
       cv::Mat m_img;
    };
    
    Foo foo(R"(C:\myImg.png)");
    auto bar = foo.GetMat();
    cv::circle(bar, cv::Point(30, 30), 5, CV_RGB(255, 0, 0)); // Kreis in bar zeichnen -> ändert Foo::m_image
    

    Meine Frage ist, wie ihr GetMat() implementieren würdet.

    const cv::Mat& GetMat() const // ist diese Methode const, obwohl außerhalb schreibend auf das Member zugegriffen werden kann?
    const cv::Mat& GetMat() // zwar nicht mehr const,  verbirgt aber, dass eine Kopie der zurückgegebenen const-Referenz Zugriff auf das Member ermöglicht
    cv::Mat& GetMat() // vielleicht die einzig konsequente Variante? Jedenfalls mein Favorit.
    

    Über ein paar Anregungen und Kommentare zu den drei Varianten würde ich mich freuen.



  • const cv::Mat& GetMat() const
    

    Ich verstehe gar nicht, warum du den getter ändern willst, weil sich irgendeine Funktion -auf die du keinen Einfluss hast- sich merkwürdig verhält.



  • Dein Vorschlag wäre also, dass ich die "Innereien" von cv::Mat einfach ignoriere und den Getter so implementiere wie ich es üblicherweise machen würde.
    Der Argumentation kann ich schon folgen, wenn man aber ein bisschen allgemeiner denkt, gefällt mir halt nicht, dass ich in einer Klasse eine const-Methode anbiete, die eine Änderung der Member ermöglicht. Thread-Safety ist in meinem konkreten Fall zwar kein Thema, aber const-Methoden werden beispielsweise gerne mal mit Thread-Safety assoziiert und solche Dinge würde ich auf diese Art wissentlich "verschleiern"...


  • Mod

    Und von einer Kopie würde man erwarten, dass sie eine Kopie macht…

    Wenn Open_CV ein Objekt als unverändert ansieht, solange sich die Hilfsdaten nicht ändern, die eigentlichen Nutzdaten aber schon, würde ich diese Semantik an deiner Stelle auch so weiter geben. Die merkwürdige Semantik kommt hier nicht von deiner Klasse.


  • Mod

    Du hast ein Paar Möglichkeiten.

    1. Den Getter irgendeine Form von Mat zurückgeben lassen. Wenn es für die interne Logik der Klasse zählt, dass m_img nicht modifiziert wird, offensichtlich nicht mit const qualifizieren. Das const an der Referenz kannste ruhig lassen. Vielleicht einen entsprechenden Kommentar über der Deklaration stehen lassen.
    2. Einen Wrapper schreiben. Das kann sowohl ein funktional vollständiger Wrapper sein (sprich, Mat komplett anbieten, aber Kopier-Semantik kohärent gestalten), oder einfach eine immutable view. Mit der letzteren Idee wären wir auch bei der nächsten angelangt:
    3. Die Getter von Mat anbieten. Falls die Meta-Informationen in Mat für die Nutzer von GetMat nicht von Bedeutung sind, sondern lediglich der Inhalt, kannst Du sie auch unmittelbar auf den Inhalt zugreifen lassen. Ist Design-technisch etwas unschöner aber immerhin sauber.
    4. Klage vor dem Bundesgerichtshof die const Correctness der Bibliothek ein.


  • Vielen Dank für die Anregungen!
    Ich denke, dann werde ich euch folgen und nicht die OpenCV-Eigenheiten zu meinen eigenen machen. Wenn man das entsprechend kommentiert, sollte dem Anwender ja auch klar werden, wie damit umzugehen ist.
    Columbos vierten Punkt lasse ich mich auch noch einmal durch den Kopf gehen 😉 .


Log in to reply