ambiguous operator overload



  • Liebes Forum,

    ich übe mich gerade in C++ und stolpere nun über Operator-Überladung und die Überladung von Funktionen an sich. Mein Problem ist, dass ich den Compilerfehler bei folgendem Code nicht verstehe:

    // test.cpp
    
    #include <iostream>
    #include <vector>
    
    // ---------------------------
    
    class A {
    public:
    	A(std::size_t size, double init) : _data(size, init) { }
    	double operator[](std::size_t index) const { return _data.at(index); }
    	double& operator[](std::size_t index) { return _data.at(index); }
    	double operator[](double x) const { return x; } // was hier innerhalb der Methode passiert ist eigentlich egal...
    private:
    	std::vector<double> _data;
    };
    
    // ---------------------------
    
    int main() {
    	A a(10, 2.25);
    	std::cout << a[1] << '\n';
    	std::cout << a[3.14] << '\n';
    	return 0;
    }
    

    Der Compiler meldet mir folgendes (g++ version 5.4.0 mit -std=gnu++11):

    test.cpp: In function ‘int main()’:
    test.cpp:35:16: error: ambiguous overload for ‘operator[]’ (operand types are ‘A’ and ‘double’)
      std::cout << a[3.14] << std::endl;
                    ^
    test.cpp:10:9: note: candidate: double A::operator[](std::size_t) const
      double operator[](std::size_t index) const {
             ^
    test.cpp:15:10: note: candidate: double& A::operator[](std::size_t)
      double& operator[](std::size_t index) {
              ^
    test.cpp:20:9: note: candidate: double A::operator[](double) const
      double operator[](double x) const {
             ^
    

    Kann mir bitte jemand den Fehler erklären und wie ich die "mehrdeutige Überladung" gefixt bekomme?

    Viele Grüße,
    Daniel



  • Probiere es mal mit -std=gnu++14



  • manni66 schrieb:

    Probiere es mal mit -std=gnu++14

    Keine gute Idee, weils dann kein ISO C++ mehr ist (der Compiler wird wahrscheinlich eine Warnung anzeigen).

    Zuerst einmal solltest du eine nicht- const Version von operator[](double x) definieren, dann ist Zeile 23 kein Problem mehr. Allerdings wird dann Zeile 22 ambiguous sein: soll 1 (vom Typ int ) nach std::size_t oder nach double gecastet werden? Das ist nicht klar.

    Versuch mal zu beschreiben was du genau machen moechtest - ich denke das Interface deiner Klasse ist so nicht sehr sinnvoll.



  • manni66 schrieb:

    Probiere es mal mit -std=gnu++14

    Das ist egal. Ob mit -std=gnu++14, c++14, gnu++98 oder c++98, eine Warnung kommt trotzdem (bzw. ein Fehler, da ich mit -Werror kompiliere). Das Problem hat glaube ich auch nichts mit dem C++14-Standard zu tun.

    icarus2 schrieb:

    Versuch mal zu beschreiben was du genau machen moechtest - ich denke das Interface deiner Klasse ist so nicht sehr sinnvoll.

    Also ich möchte zur Übung eine Klasse measured_data schreiben, die Messdaten speichert (in den privaten Elementen std::vector<double> x_data, y_data ) und zwischen diesen auch linear interpoliert. Weiterhin möchte ich auf die Datenpunkte an sich lesend und schreibend zugreifen können, daher habe ich im Moment folgendes:

    // Auslesen einzelner Datenpunkte
    double measured_data::operator[](std::size_t index) const {
        return y_data.at(index);
    }
    
    // Zuweisung "measured_data dat; dat[0] = 3.1;" ermöglichen
    double& measured_data::operator[](std::size_t index) {
        return y_data.at(index);
    }
    

    Und meine Idee war nun, die Interpolation zwischen den Datenpunkten wie folgt zu implementieren

    double measured_data::operator[](double x) const {
        return linear_interpolation.at(x);
    }
    

    um bei einem Aufruf der Form dat[3.1] zu ermöglichen. Und da kam dann halt die Sache mit dem ambiguous operator overload auf.


  • Mod

    Moment, das ist echt? Ich dachte, dass wäre extra ein Beispiel, um gezielt diese etwas obskure C++-Regel zu triggern 😮

    Es liegt letztlich daran, dass die const nicht symmetrisch verteilt sind. Wenn du noch einen non-const Variante für double hinzu fügst, sollte es gehen (wobei dann der Aufruf mit int ambiguous wird, aber das ist leicht zu beheben). In deinem Fall macht dies aber keinen Sinn, eine non-const Version für die double-Überladung einzufügen. Das liegt da dran, dass du hier Überladung benutzt hast, nicht um wie vorgesehen verschiedene Varianten der gleichen Funktionalität zu haben, sondern um zwei völlig unterschiedliche Funktionalitäten unter gleichem Namen zu haben. Das ist natürlich nicht so günstig. Der wahre Ratschlag ist daher keine technische Lösung für die korrekte Überladungsauflösung, sondern der Hinweis, dass du die unterschiedlichen Funktionalitäten lieber unterschiedlich benennen solltest.



  • In Anlehnung an das, was SeppJ gesagt hat, wäre es vielleicht auch nicht verkehrt noch ein wenig mehr zu abstrahieren die Funktionalität "Interpolation" in eine weitere Klasse auszulagern,
    die z.B. die Messwerte-Instanz referenziert und das gewünschte double-Interface zur Verfügung stellt, so dass es sich dann in etwa so anwenden lässt:

    measured_data data;
    linear_interpolated_data interpolated_data{ data };
    double point = interpolated_data[3.1];
    

    So ließen sich auch andere Interpolationsmethoden einfach als weitere Interpolationsklassen implementieren, ohne dass diese alle in measured_data implementiert werden müssen.

    Finnegan

    P.S.: Noch eine Anmerkung: Außerhalb von eindimensionalen, array-artigen Container-Klassen mit Integer-Index bin ich nicht unbedingt ein Fan des operator[]. Ich halte da eine
    Member-Funktion oder auch den operator() für wesentlich flexibler. Besonders weil es auch multivariate Funktionen/Messwerte ohne zusätzliches Kopfzerbrechen erlaubt.
    Vielleicht benötigst du ja irgendwann auch mal einen

    double point = interpolated_data(8.7, 3.1, -2.8);
    

    ?


Log in to reply