Compiler will privaten operator[] anstatt des öffentlichen verwenden



  • Hi, ich habe schon öfters das Problem gehabt, dass der Compiler anstatt des öffentlichen Operators der wunderbar hätte verwendet werden können den privaten hernehmen möchte, was mit einer entsprechenden Fehlermeldung quittiert wird. Es ist als wäre durch die nachfolgende Deklaration des privaten Operators der öffentliche "unsichtbar" geworden. Habe deswegen auch schon mit using versucht den öffentlichen Op wieder sichtbar zu machen was mir allerdings nicht gelungen ist. Jemand eine Idee warum der Compiler den öffentlichen Operator ignoriert?

    #include <iostream>
    
    using namespace std;
    
    class Integers
    {
    public:
    	Integers(int* values) { _values = values; }
    	~Integers() { delete[] _values; };
    
    	const int& operator[](size_t index) const
    	{
    		return _values[index];
    	}
    	
    	operator const int*() const
    	{
    		return _values;
    	}
    	
    private:
    	int& operator[](size_t index)
    	{
    		return _values[index];
    	}
    	
    	operator int*() const
    	{
    		return _values;
    	}
    	
    	int* _values;
    };
    
    int main()
    {
    	int* values = new int[5] { 0, 1, 2, 3, 4 };
    	Integers ints(values);
    	const int& i = ints[2];
    	printf("int: %d", i);
    	return 0;
    }
    

    Siehe auch:
    https://ideone.com/30K8dl

    prog.cpp: In function ‘int main()’:
    prog.cpp:39:23: error: ‘int& Integers::operator[](size_t)’ is private within this context
      const int& i = ints[2];
    

    Ich habe sogar eine const Reference i verwendet um dem Compiler den Weg zu zeigen...



  • Der ist const, und dein Kontext ist nicht const.



  • Ich glaube, deine Verwirrung kommt von folgendem. Der Compiler sucht sich ZUERST die Funktion, die von der Signatur her am besten passt (dabei Sichtbarkeiten ignorierend) und schaut erst DANACH auch die Sichtbarkeit, d.h. ob die gefundene Funktion public oder private ist.

    Das heißt, der Titel "Compiler will privaten operator[] anstatt des öffentlichen verwenden" sollte besser "Compiler will non-const operator[] anstelle des const operator[] verwenden" heißen.

    Hier ist ints nicht const, also passt der operator[] ohne const besser. Funktionsüberladung / Auswahl des Overloads funktioniert NICHT nach dem Wert, dem zugewiesen wird.

    Du kannst z.B. schreiben:

    const auto &iref = ints;
    // iref ist const, also wird der operator[](size_t) const aufgerufen.
    const int& i = iref[2];
    

    Mit C++17 und dem <utility> Header geht auch:

    const int &i = std::as_const(ints)[2];
    


  • Genau daher ist Sichtbarkeit auch eine problematische Bezeichnung dafür, weil es eben nicht um Sichtbarkeit geht.

    Ein weiterer viel verbreiteter Irrglaube ist, dass man "private" Sachen grundsätzlich niemals nicht von Ausserhalb verwenden kann. Wobei ich jetzt nicht friend meine, vergessen wir mal friend. Und auch das "Einbrechen" via. Spezialisierung von Memberfunktions-Templates meine ich nicht.

    Was ich meine ist sowas:

    class Foo {
    private:
        class PrivateNestedBar {
        public:
            PrivateNestedBar(int i) : i{i} {}
            void very() {}
            void nasty() {}
            int i;
        };
    public:
        PrivateNestedBar funfunfun() {
            return PrivateNestedBar{ 123 };
        }
    };
    
    void outside() {
        Foo foo;
    
        auto pnb = foo.funfunfun();
        pnb.very();
        pnb.nasty();
    
        auto anotherPnb = decltype(pnb){ 42 };
        anotherPnb.very();
        anotherPnb.nasty();
    }
    
    // Oder sogar
    
    #include <type_traits>
    
    using PrivateNestedBar = decltype(std::declval<Foo>().funfunfun());
    
    PrivateNestedBar evenMoreFun() {
        PrivateNestedBar pnb{ 23 };
        pnb.very();
        pnb.nasty();
        return pnb;
    }
    

    Alles ganz legales C++.

    Heisst: private heisst nicht dass es verboten ist das private Ding ausserhalb zu verwenden. Es heisst nur dass es verboten ist das Ding ausserhalb über den privaten "Namen" zu "benennen".

    Wenn eine Klasse - im Gesensatz zu der hier gezeigten Klasse Foo - keine "Löcher" hat, also keine anderen Wege existieren wie man das private Ding "benennen" kann, dann führt private effektiv dazu dass man das private Ding auch nicht verwenden kann. Genau so wie man keine Dinge in anonymen Namespaces anderer Übersetzungseinheiten verwenden kann, da man ihre Namen nicht nennen kann.

    Oder kurz gesagt: private bezieht sich auf den Weg wie man zu dem Ding kommt, nicht aber auf das Ding selbst.


Log in to reply