ODR: Konstanten und Inlinefunktionen im Header



  • Dies war eigentlich als Antwort auf Tomahawk's Frage gedacht. Der Thread wurde schon geschlossen. Das habe ich aber leider erst dann gemerkt, als ich die geschriebene Antwort abschicken wollte. Ich denke Tomahawks Frage war berechtigt und sein Beispiel mit Konstanten und inline-Funktionen hat mich dazu veranlasst, eine ODR-Falle zu erwähnen, welche nicht ganz uninteressant sein dürfte:

    Tomahawk schrieb:

    Wandelt der Compiler nachfolgende Definition von Konstanten automatisch in eine Zahl um, so dass ich in den Inline Functions mit den Konstanten (noch auskommentiert) statt den unlesbaren Hexzahlen arbeiten darf, ohne dass etwas langsamer wird?

    Weil der Wert von solchen Konstanten feststeht und sie nicht zur Laufzeit geändert werden dürfen, ist es einem Compiler erlaubt, die Namen der Konstanten an Stellen, wo Rvalues erwartet werden, direkt durch die Werte zu ersetzen, ohne dafür extra eine Konstante im Speicher anzulegen. Erst wenn die Adresse eines solchen Objekts benutzt wird, muss der Compiler auch Speicher für die Konstante anlegen. Diesbezüglich muss man aber etwas aufpassen:

    Irgendeine Header-Datei:

    #ifndef SOME_HEADER_INCLUDED
    #define SOME_HEADER_INCLUDED
    
    #include <vector>
    
    const int dings = 31415;
    
    inline void bums(std::vector<int> & v) {
      v.push_back(dings);
    }
    
    #endif // SOME_HEADER_INCLUDED
    

    Hier gibt es eine Verletzung der ODR (one definition rule), falls diese Headerdatei in mehreren Übersetzungseinheiten eingebunden wird. dings hat eine interne Bindung, bums aber nicht. Die ODR erlaubt die Mehrfachdefinition von Funktionstemplates und Inline-Funktionen (höchstens eine pro ÜE aber insgesamt auch mehr als eine), verlangt aber, dass die Definition dieselbe ist, wobei sich auch alle verwendeten Bezeichner auf dieselbe Entität beziehen, falls mehr als nur ihr Wert verwendet wird. In diesem Beispiel biezieht sich aber dings immer auf ein anderes Objekt, da jede ÜE ihr eigenes dings bekommt. Die push_back -Funktion nimmt ihren Parameter als Referenz-auf-const entgegen, was eine Adresse erfordert. Das führt dazu, dass die Definitionen von bums über versch ÜEs nicht mehr dieselben sind --> Verletzung der ODR.

    Die Variation

    inline void bums(std::vector<int> & v) {
      v.push_back(dings+0);
    }
    

    erzeugt stattdessen ein temporäres int-Objekt und übergibt die Adresse der push_back-Funktion. Hier wird die ODR nicht mehr verletzt.

    Gruß,
    SP


  • Mod

    Sebastian Pizer schrieb:

    Die push_back -Funktion nimmt ihren Parameter als Referenz-auf-const entgegen, was eine Adresse erfordert.

    Da bin ich nicht so sicher.


Anmelden zum Antworten