operator[]



  • Der operator[] ist nur in einer Klasse verwendbar. Diese Klasse "vector" sieht mal etwa so aus:

    template<class T> class Vector {
    public:
      typedef T* iterator;
    
      // Primitive C'tor and D'tor
    
      Vector() : m_capacity(0), m_size(0), m_buffer(NULL) {}
    
      Vector(int size) : m_size(size), m_capacity(size), m_buffer(new T[m_capacity]) {}
    
      ~Vector() {
        delete[] m_buffer;
      }
    
      // operator[]
    
      T& operator[](int index) {
        return m_buffer[index];
      }
    
      const T& operator[](int index) const {
        return m_buffer[index];
      }
    
    private:
      int m_size, m_capacity;
    
      T* m_buffer;
    };
    

    Nur dass in folgendem immer die "non-const" Funktion aufgerufen wird:

    int main() {
      Vector<int> v(10);
    
      v[0] = 1;  // non-const OK
      v[1] = 2;  // non-const OK
      v[2] = v[0] + v[1];  // Warum non-const beim lesen in V[0] und v[1]?
    }
    

    Wie kann ich forcieren, dass die "const" operator[] Funtion verwendet wird?

    😕



  • Tomahawk schrieb:

    Wie kann ich forcieren, dass die "const" operator[] Funtion verwendet wird?

    Indem du den Vector const machst.



  • Oder indem du den Vektor in eine const lvalue reference auf einen Vektor castest: static_cast<Vector<int> const&>(v)[0];

    Aber es ist schon gut so, dass hier die non-const-Methode aufgerufen wird. Darf ich mich nach dem Hintergrund deiner Frage informieren?

    Und woher soll der Compiler überhaupt wissen, dass nur gelesen wird?

    void f(int const&, int const&);
    int f(int&, int&);
    int a = f(v[0], v[1]); // Kompiliert? Oder doch Fehler weil void -> int?
    // ...
    void g(int&, int const&);
    void g(int const&, int&);
    g(v[0], v[1]); // Kompiliert? Wenn ja, welches g wird aufgerufen? Oder doch ambiguos?
    


  • asfdlol schrieb:

    Oder indem du den Vektor in eine const lvalue reference auf einen Vektor castest: static_cast<Vector<int> const&>(v)[0];

    Aber es ist schon gut so, dass hier die non-const-Methode aufgerufen wird. Darf ich mich nach dem Hintergrund deiner Frage informieren?

    Das Analysetool "QACPP" von "qa-systems" meckert herum, dass die Ergebnisse einer Berechnung nicht verhersehbar sind, zB v[0] = v[1] + v[2];

    Da auf den non-const operator[] zugegriffen wird, könnte während der Zuweisung der Wert verändert werden...



  • Was sagt diese Tool bei operator[] des std::vector? Der ist genauso definiert.



  • Tomahawk schrieb:

    Das Analysetool "QACPP" von "qa-systems" meckert herum, dass die Ergebnisse einer Berechnung nicht verhersehbar sind, zB v[0] = v[1] + v[2];

    Da auf den non-const operator[] zugegriffen wird, könnte während der Zuweisung der Wert verändert werden...

    Pclint meckert solche Sachen auch gerne an. Ich mach dann meistens 2 Zeilen draus und schreibe je nach Laune noch einen Kommentar der die Macher von dem Tool beleidigt dazu.


  • Mod

    Tomahawk schrieb:

    asfdlol schrieb:

    Oder indem du den Vektor in eine const lvalue reference auf einen Vektor castest: static_cast<Vector<int> const&>(v)[0];

    Aber es ist schon gut so, dass hier die non-const-Methode aufgerufen wird. Darf ich mich nach dem Hintergrund deiner Frage informieren?

    Das Analysetool "QACPP" von "qa-systems" meckert herum, dass die Ergebnisse einer Berechnung nicht verhersehbar sind, zB v[0] = v[1] + v[2];

    Da auf den non-const operator[] zugegriffen wird, könnte während der Zuweisung der Wert verändert werden...

    Komische Analyse. Da ist schließlich eine Zuweisung drin, natürlich verändert sich dadurch der Inhalt des Containers. Und der Aufruf der const-Überladungen würde auch nichts daran ändern, dass das Ergebnis der Berechnung u.U. von vorherigen Zuweisungen abhängt (wenn ich mal unterstelle, dass noch mehr solche Zuweisungen mit unterschiedlichen Indizes stattfinden). Könnte es also sein, dass das Tool etwas anderes bemängelt?

    wenn man sowas öfter braucht, kann man sich auch eine Hilfsfunktion schreiben

    template <typename T>
    const T& constify(T& x) { return x; }
    
    v[0] = constify(v)[1] + constify(v)[2];
    

    erzeugt etwas weniger "Lärm" als ständig explizite (und harmlose) const_casts zu verwenden.



  • @camper
    Der non-const operator [] könnte ja total plem implementiert sein...

    T& operator [](size_t n)
    {
        if (n == 2)
            std::fill(begin(), end(), T());
        return m_storage[n];
    }
    

    Und dann wäre, auf Grund der nicht definierten Auswertungsreihenfolge der Operanden v[1] und v[2] , das Ergebnis nicht deterministisch.

    Um hier weniger Noise zu erzeugen müsste ein Static Analysis Tool entweder

    1. Auf diesen Check verzichten
    2. Ausnahmen anhand des Namen oder Name+Signatur der Funktion machen (also in diesem Fall für alle Funktionen die "operator[]" heissen)
    3. Analysieren ob die Funktion wirklich selbst den Zustand des Objekts ändert
      Andere Möglichkeit fällt mir jetzt auf die Schnelle keine ein.

    (1) wäre schade und (2) und (3) sind irgendwie beide nicht optimal. Bei (2) könnten Fehler ignoriert werden die sich unabsichtlich eingeschlichen haben, und bei (3) könnte man sowohl False Positives wie auch False Negatives bekommen.

    False Positives z.B. wenn die Funktion irgendwelche "Nebeneffekte" hat die aber den beobachtbaren State nicht ändern -- z.B. die aktualisierung eines Caches.
    (OK, die könnte man vermutlich los werden indem man Änderung von mutable Membern erlaubt.)
    False Negatives z.B. wenn die konkrete Implementierung einer Klasse zwar nichts ändert, aber eine Änderung sehr wohl erlaubt wäre.


Anmelden zum Antworten