vector<const...>



  • Hallo!

    Ich will Übergabeparameter für eine Methode consten. Das sieht so aus:

    void Visualizer::writeTriangles(vector<const Triangle* const>& vTriangles) const
    {
      for(vector<const Triangle* const>::iterator it=vTriangles.begin();it!=vTriangles.end();++it)
      {
        bla..;
      }
    }
    

    Damit will ich sagen, daß sowohl die Pointer im Vektor als auch die Inhalte, auf die sie zeigen, konstant sind. Dann läßt sich aber nicht mehr über den
    Vector iterieren, denn:

    /usr/include/c++/4.4/ext/new_allocator.h: In instantiation of ‘__gnu_cxx::new_allocator<const Triangle* const>’:
    /usr/include/c++/4.4/bits/allocator.h:87: instantiated from ‘std::allocator<const Triangle* const>’
    /usr/include/c++/4.4/bits/stl_vector.h:71: instantiated from ‘std::_Vector_base<const Triangle* const, std::allocator<const Triangle* const> >’
    /usr/include/c++/4.4/bits/stl_vector.h:171: instantiated from ‘std::vector<const Triangle* const, std::allocator<const Triangle* const> >’
    Visualizer.cpp:126: instantiated from here
    /usr/include/c++/4.4/ext/new_allocator.h:79: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(const _Tp&) const [with _Tp = const Triangle* const]’ cannot be overloaded
    /usr/include/c++/4.4/ext/new_allocator.h:76: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(_Tp&) const [with _Tp = const Triangle* const]’

    Wo liegt nun der Denkfehler?

    Danke, lg



  • Alle Standardcontainer erwarten, dass der Werttyp Assignable ist. T const *const ist das nicht.

    Was du tun kannst, ist

    vector<Triangle const *> const &vTriangles
    

    übergeben und

    vector<Triangle const *>::const_iterator
    

    benutzen.



  • > Wo liegt nun der Denkfehler?

    Entweder glaubst Du, dass man vector<const X> für irgend einen Typen X verwenden kann (dem ist nicht so), oder Du hast Dich in der Position des zweiten const s geirrt.

    Wolltest Du vielleicht

    vector<const Triangle*> const& vTriangles
    

    statt

    vector<const Triangle* const>& vTriangles
    

    verwenden?



  • davon ab macht selbst "vector<const Triangle*>" als Parameter mit fast 100% Wahrscheinlichkeit nicht das, was du willst. Ich vermute, dass du sowas machen möchtest:

    vector<Triangle*> triangles;
    //...
    //wie auch immer dein Code aussieht...nur als Beispiel
    triangles[0]->point1=neuerPunkt();
    //...
    visualizer.writeTriangles(triangles);
    

    Das würde aber nicht klappen, da der vector "Triangle*" enthält und nicht "const Triangle*". Der Parameter muss dann "vector<Triangle*> const &" sein, damit das funktioniert.

    Eine echte Lösung gibt es für dein Problem leider nicht. Das ist ein Mangel der Sprache. Weniger Probleme hast du mit "vector<Triangle> const &", dann sind auch die Elemente unveränderlich und alles funktioniert blendend.



  • otze schrieb:

    Weniger Probleme hast du mit "vector<Triangle> const &", dann sind auch die Elemente unveränderlich und alles funktioniert blendend.

    Die Elemente ja - das worauf sie zeigen, nämlich die eigentlich interessanten Triangle-Objekte nicht...

    /edith meint ich soll genauer lesen, dann schreiben...

    otze schrieb:

    deshalb sind dort die Elemente auch keine Zeiger 😉



  • deshalb sind dort die Elemente auch keine Zeiger 😉



  • otze schrieb:

    Eine echte Lösung gibt es für dein Problem leider nicht.

    Vielleicht ist ja ein boost pointer container besser geeignet. Sonst kann man noch einen "transform iterator" benutzen:

    #include <iostream>
    #include <vector>
    #include <boost/iterator/transform_iterator.hpp>
    
    template<class Iter>
    void show(Iter beg, Iter end)
    {
    	while (beg!=end) {
    		std::cout << ' ' << *beg;
    		++beg;
    	}
    	std::cout << '\n';
    }
    
    template<class Result>
    struct deref
    {
    	typedef Result& result_type;
    	template<class PointerLike>
    	Result& operator()(PointerLike const& p) const { return *p; }
    };
    
    template<class DerefAs, class Iter>
    boost::transform_iterator<deref<DerefAs>,Iter>
    make_deref_iter(Iter it)
    {
    	typedef deref<DerefAs> fun;
    	return boost::transform_iterator<fun,Iter>(it,fun());
    }
    
    int main()
    {
    	int i = 23;
    	int j = 42;
    	std::vector<int*> intptrs;
    	intptrs.push_back(&i);
    	intptrs.push_back(&j);
    	// show bekommt Iteratoren, die die Zeiger in intptrs
    	// dereferenzieren und eine const-Referenz liefern
    	// show kann damit i und j nicht ohne weiteres ändern.
    	show(make_deref_iter<const int>(intptrs.begin()),
    	     make_deref_iter<const int>(intptrs.end()  ) );
    }
    

    kk



  • Ich finds Schade, dass das im Standard bei Zeigern versäumt wurde. für mich fühlt es sich natürlich an, dass wenn "x ist const" gilt auch "x->y ist const" gilt( oder Eingeschränkt: "ist z const und enthält z einen nicht mutable Member z.x dann ist auch z.x->y const" Aber ein ähnliches Problem gibt es ja auch bei Iteratoren, weswegen wir einen const_iterator brauchen 😞



  • seldon schrieb:

    Alle Standardcontainer erwarten, dass der Werttyp Assignable ist. T const *const ist das nicht.

    Was du tun kannst, ist

    vector<Triangle const *> const &vTriangles
    

    übergeben und

    vector<Triangle const *>::const_iterator
    

    benutzen.

    Danke allen für die Antworten. Das obige ist die Lösung. Ich kannte bloß den ::const_iterator noch nicht. Wieder was gelernt.

    lg



  • Man kann sowas auch "elegant" mit Iterator-Ranges und einem Template lösen:

    class Visualizer
    {
    public:
        template <class Iterator>
        void WriteTriangles(Iterator begin, Iterator end) const
        {
            for(; begin != end; ++begin)
                Blah(*begin);
        }
    
    private:
        void Blah(Triangles const* triangle) const; // kein template und kann daher in der .cpp implementiert werden
    };
    

    So einer WriteTriangles() Funktion könnte man dann sowohl Iteratoren auf einen vector<Triangles const*> als auch auf einen vector<Triangles*> übergeben. Oder rohe Arrays oder ganz andere Container verwenden.



  • hustbaer schrieb:

    Man kann sowas auch "elegant" mit Iterator-Ranges und einem Template lösen:

    template <class Iterator>
    void WriteTriangles(Iterator begin, Iterator end) const
    {
        for(; begin != end; ++begin)
            Blah(*begin);
    }
    
    void Blah(Triangles const* triangle) const; // kein template und kann daher in der .cpp implementiert werden
    

    Ja. Das ist keine schlechte Idee. So kann man auch "Template-Bloat" reduzieren, in dem man das, was sich für verschiedene Spezialisierungen nicht unterscheidet in ein nicht-Template (wie Blah) auslagert.

    ...wobei ich aber auch daran zweifle, wie sinnig es ist, einen container<T> mit T = const Triangle* zu verwenden. Ich hoffe, dass der nicht so zustande gekommen ist:

    vector<Triangle*> ptrvec;
      ...
      vector<Triangle const*> const& cref = reinterpret_cast<vector<Triangle const*>&>(ptrvec);  
      dings(cref.begin(),cref.end());
    

    (Nicht nachmachen! Es ruft undefiniertes Verhalten hervor nach §3.10/15!)

    Ein vector<Triangle*> mag durch aus sinnvoll sein. Und wenn dann jemand an einer unveränderlichen Triangle-Sequenz interessiert ist, sind Iteratoren, die ich zB mit make_deref_iter erzeugt habe (siehe oben), doch sehr praktisch. Ich versuche jedenfalls Iteratoren, die bei der Dereferenzierung Zeiger liefern, zu vermeiden.

    kk


Anmelden zum Antworten