Template-Spezialisierung (Speicherverbrauch ermitteln)



  • Ich möchte eine Funktion schreiben, die mir den ungefähren Speicherverbrauch eines Objekts zurückgibt. Für triviale Typen t wird einfach sizeof(t) zurückgegeben:

    template <class T>
    typename std::enable_if<std::is_trivial<T>::value, size_t>::type memory(const T& obj) {
        return sizeof(obj);
    }
    

    Für std::deque könnte die Implementierung so aussehen:

    template <class U>
    size_t memory(const std::deque<U>& obj) {
        size_t sum = sizeof(obj);
        for (const U& u: obj) {
            sum += memory(u);
        }
        return sum;
    }
    

    Der berechnete Wert ist nicht exakt, aber für meine Zwecke reicht das aus. Mein Problem ist nun, dass der Compiler die Definition der memory-Funktion manchmal nicht "findet":

    template <DimType DIMENSION, class T>
    class QuadLeafManager {
    public:
        struct FileInterval {
            //...
        }
    }
    
    template <DimType DIMENSION, class T>
    size_t memory(const typename QuadLeafManager<DIMENSION, T>::FileInterval& fint) {
        return sizeof(fint);
    }
    

    In der Überladung von memory(QuadLeafManager) wird memory(std::vector<FileInterval>) aufgerufen:

    error: no matching function for call to 'memory(const External::QuadLeafManager<4u, float>::FileInterval&)'
             sum += memory(u); // <- dies ist der Aufruf in memory(std::vector<T>)
    

    Möglicherweise ist mein Ansatz schon verkehrt. Am liebsten hätte ich die Funktion standardmäßig als sizeof(...) definiert und sie bei Bedarf (std::vector, usw.) überladen. Wenn ich das richtig verstanden hab, kann man Funktionstemplates nicht partial spezialieren.

    Wäre es besser memory() als Klasse zu deklarieren, in etwa so:

    template <class T>
    struct Memory {
        static size_t get(const T& t) {
            return sizeof(t);
        }
    };
    
    template <class U>
    struct Memory<std::vector<U>> {
        static size_t get(const std::vector<U>& v) {
            size_t sum = sizeof(v);
            for (const U& u: v) {
                sum += Memory<U>::get(u);
            }
            return sum;
        }
    };
    

    Der Aufruf kann dann aber etwas nervig sein, da man immer den Type angeben muss:

    Memory<std::vector<int>>::get(xyz);
    

    Wie implementiert man mein Vorhaben am Besten?



  • Du musst schon angeben, was in welchem Namespace liegt. Und was hat deque mit dem Problem zu tun, wenn es um vector geht?


  • Mod

    Ramanujan schrieb:

    template <class T>
    typename std::enable_if<std::is_trivial<T>::value, size_t>::type memory(const T& obj) {
        return sizeof(obj);
    }
    

    Ich nehme mal an, dieses Template befindet sich im globalen Namensraum

    Ramanujan schrieb:

    Mein Problem ist nun, dass der Compiler die Definition der memory-Funktion manchmal nicht "findet":

    template <DimType DIMENSION, class T>
    class QuadLeafManager {
    public:
        struct FileInterval {
            //...
        }
    }
    
    template <DimType DIMENSION, class T>
    size_t memory(const typename QuadLeafManager<DIMENSION, T>::FileInterval& fint) {
        return sizeof(fint);
    }
    

    In der Überladung von memory(QuadLeafManager) wird memory(std::vector<FileInterval>) aufgerufen:

    error: no matching function for call to 'memory(const External::QuadLeafManager<4u, float>::FileInterval&)'
             sum += memory(u); // <- dies ist der Aufruf in memory(std::vector<T>)
    

    Die Variante für triviale Typen kann im Allgemeinen nicht per ADL gefunden werden. In einem Aufruf der Form

    sum += memory(u);
    

    muss folglich das normale unqualifizierte Lookup erfolgreich sein. Befinden wir uns aber im Namensraum External, und befindet sich in External eine weitere Deklaration einer memory-Funktion, verdeckt diese nat. das Template im globalen Namensraum.
    Die übliche Verfahrensweise ist hier, die Deklarationen, die nicht per ADL gefunden werden, per using-Deklaration in der Funktion bekannt zu machen.
    Also

    template <class U>
    size_t memory(const std::deque<U>& obj) {
        using ::memory; //
        size_t sum = sizeof(obj);
        for (const U& u: obj) {
            sum += memory(u);
        }
        return sum;
    }
    

    Das ist die gleiche Herangehensweise die auch bei der Verwendung von swap genutzt werden sollte.



  • Hm, irgendwie bin ich gerade verwirrt. Wenn ich versuche, den Fehler durch ein Minimalbeispiel zu reproduzieren, kompiliert alles fehlerfrei.

    Mit dem "using ::memory" hilft schon mal nicht.

    Noch eine andere Frage:
    Was kann ich machen, wenn ich möchte, dass memory(T& t) standardmäßig sizeof(t) zurückgibt?

    Edit:
    ok, jetzt geht alles, auch wenn ich noch nicht genau vestehe warum.


Anmelden zum Antworten