compare predikat für eine map initialisierung compiliert nicht wenn die argumente const referenzen sind



  • Hallo,

    Ich lese gerade ein Buch zu C++ und das hier kompiliert nicht. Ich habe bereits Artikel zu den const qualifiern gelesen aber die erklären das nicht. Die Fehler habe ich in Kommentare darüber geschrieben, kann mir jemand erklären warum das nicht kompiliert?

    #include <iostream>
    #include <vector>
    #include "NamespaceTest.h"
    #include <iterator>
    #include <string>
    #include <algorithm>
    #include <map>
    
    // vscode error: the object has type qualifiers that are not compatible with the member function "NamespaceTest::get_member"
    bool compare_predicate(const NamespaceTest& a, const NamespaceTest& b) {
    	return a.get_member() <= b.get_member();
    }
    
    int main(){
    	NamespaceTest a = NamespaceTest(1,2.0);
    	NamespaceTest b = NamespaceTest(2,2.0);
            //error: no instance of constructor ... matches the argument list.
    	std::map<NamespaceTest, int> name_vec(compare_predicate);
    
    	return 0;
    }
    
    #include "NamespaceTest.h"
    #include "CircularIncludeTest.h"
    
    int NamespaceTest::get_member(){
        return member;
    }
    
    double NamespaceTest::get_member2(){
        return member2;
    }
    
    NamespaceTest::NamespaceTest(int a, double b): member(a), member2(b){};
    
    #ifndef GUARD_namespacetest_h
    #define GUARD_namespacetest_h
    
    class NamespaceTest {
    		public:
    		NamespaceTest(int, double);
    
    		int get_member();
    
            double get_member2();
    
    		private:
    		int member;
    		double member2;
    	};
    
    #endif
    


  • Du mußt dann deine Member als const deklarieren:

    int get_member() const;
    

    sowie

    int NamespaceTest::get_member() const
    {
        return member;
    }
    

    (gleiches für get_member2).

    Stichwort: const correctness

    Edit:
    Außerdem muß die Vergleichsfunktion bei std::map<...> Strict Weak Ordering implementieren, d.h. < anstatt <=:

    bool compare_predicate(const NamespaceTest& a, const NamespaceTest& b) 
    {
    	return a.get_member() < b.get_member();
    }
    

  • Mod

    Fehler Nummer 1: Du musst deine get_member-Funktionen als const deklarieren, sonst sieht das so aus, als würden die etwas an NamespaceTest ändern können, wodurch sie auf einem konstanten Objekt disqualifiziert sind. Also

    class NamespaceTest {
    		public:
    		NamespaceTest(int, double);
    
    		int get_member() const;
    
                    double get_member2() const;
    
    		private:
    		int member;
    		double member2;
    	};
    
    [...]
    	
    int NamespaceTest::get_member() const {
        return member;
    }
    
    double NamespaceTest::get_member2() const{
        return member2;
    }
    

    Fehler 2 ist etwas komplizierter. So wie du das schreibst, kann sich der Compiler nicht zurecht reimen, dass das der Comparator sein soll. Du musst in der Templateliste angeben, welchen Typ dein Comparator haben soll, dann merkt er auch wenn du einen Comparator zu übergeben versuchst. Zum Beispiel:

    	std::map<NamespaceTest, int, bool(*)(const NamespaceTest&, const NamespaceTest&)> name_vec{compare_predicate};
    

    (es gibt auch andere Varianten davon, aber ich mache es mal so explizit wie möglich)

    Ab C++17 könnte man auch CTAD benutzen, aber das ist auch nicht der Weisheit letzter Schluss, weil es sehr alles oder nichts ist. Das ist bei einer Map oft ungünstig, denn man müsste einen Inhalt für den Anfang angeben (damit die Inhaltstypen erschlossen werden können), aber oft will man Maps zu Beginn leer haben.



  • Danke für eure Antworten, sehr hilfreich. Und was ist, wenn ich die Variablen von

    get_member()
    

    nicht konstant haben will, weil man sie eben doch ändern kann, aber in dem comparator sollen sie nicht geändert werden, also sollte da

    const NamespaceTest& a, ...
    

    stehen. Aber das geht nicht oder?


  • Mod

    Nein, das geht nicht, denn dann würden ja die Vergleiche, welche die Map durchführt, die Werte in der Map ändern, und die ganze Logik bricht in sich zusammen.

    Aber ich bin ziemlich sicher, dass du stattdessen komplett missverstehst, was die const-Qualifizierung an den Funktionen macht.

    class X {
        int get_member() const;
    };
    

    macht überhaupt keine Aussage über die const'ness der Member von X. Es heißt nur, dass speziell die Funktion get_member nichts an den Werten verändern darf.



  • Reine Getter sollten immer konstant sein.
    Wenn du doch mal Member innerhalb von konstanten Funktionen ändern willst (z.B. wenn Caching o.ä. benutzt wird), dann kannst du diese als mutable deklarieren.


  • Mod

    Da muss man aber wirklich, wirklich verstehen, was man tut, bevor man etwas mit mutable in einen assoziativen Container wirft. Bloß nicht zuhause nachmachen, bloß um Code zum compilieren zu bekommen, der (zurecht) nicht compilieren will!


  • Banned

    @fairy2211 sagte in compare predikat für eine map initialisierung compiliert nicht wenn die argumente const referenzen sind:

    #include "NamespaceTest.h"

    Vertue ich mich, oder ist hier die Konvention "hpp"?


  • Mod


  • Banned

    and it’s easier to name all headers .h instead of having different extensions for just those headers that are intended to be shared with C.

    Sorry, aber das ist für mich kein Argument dafür, um unsauber zu arbeiten. Und gewisse C++-Koryphäen widersprechen auch.

    Was, wenn ein Architekt sagen würde, jo, eine Hundehütte genügt ja auch und ist einfacher zu bauen? Glaube, das würde keinem gefallen, der keine Hundehütte braucht.



  • @sepe Du hast nach Konvention gefragt. Wenn die C++ Core Guidlines was anderes nutzen ist es unwahrscheinlich, dass das eine Konvention ist, an die man sich halten müsste, oder?


  • Mod

    @Schlangenmensch sagte in compare predikat für eine map initialisierung compiliert nicht wenn die argumente const referenzen sind:

    @sepe Du hast nach Konvention gefragt. Wenn die C++ Core Guidlines was anderes nutzen ist es unwahrscheinlich, dass das eine Konvention ist, an die man sich halten müsste, oder?

    Bloß zur Info: sepe ist cyborg_beta, omggg, oder wie er sonst noch so hieß. Damit du nicht zu viel Zeit verschwendest mit gut gemeinten Antworten auf Getrolle.


  • Banned

    Offenbar gibt es da keine eindeutige Definition/Konvention, an die man sich halten soll.


Log in to reply