Opened 8 years ago

Closed 8 years ago

#11139 closed Bugs (fixed)

boost::container::vector<std::shared_ptr<const T>, std::allocator<T>>::const_iterator allows changing its dereferenced elements

Reported by: matteo.settenvini@… Owned by: Ion Gaztañaga
Milestone: To Be Determined Component: container
Version: Boost 1.57.0 Severity: Problem
Keywords: Cc: felix.klar@…

Description

Consider the following attempt to make a const_iterator for a vector of smart pointers, be de-referentiable only to smart pointers to constant types:

#include <memory>

#include <boost/container/vector.hpp>

class C
{
public:
    using a_type = int;

    using shared_ptr = std::shared_ptr<a_type>;
    using shared_constptr = std::shared_ptr<const a_type>;
    
    // In my code, I am using a more complex allocator
    // in shared memory from boost::interprocess.
    using some_fixed_allocator = std::allocator<shared_ptr>;

    using vector = boost::container::vector<shared_ptr, some_fixed_allocator>;
    using cvector = boost::container::vector<shared_constptr, some_fixed_allocator>;

    C ()
    {
        shared_ptr p = std::make_shared<a_type> (1);        
        _v.push_back (p);        
    }

    cvector::const_iterator first_element () const
    {
        return _v.begin ();
    }
    
    
private:
    vector _v;
};


int
main ()
{
    C c;
    
    C::cvector::const_iterator it = c.first_element ();
    **it = 2; // Expecting an error!

    // Note that we have an error using std::vector.
}

This compiled cleanly with G++ 4.9 with -std=c++11, which really is hiding the problem as the const_iterator allows changing elements of a the cvector type.

Using a std::vector at least results in the following compilation error:

allocator-rebind.cc: In member function ‘std::vector<boost::shared_ptr<const int>, std::allocator<int> >::const_iterator C::first_element() const’:
allocator-rebind.cc:31:26: error: could not convert ‘((const C*)this)->C::_v.std::vector<_Tp, _Alloc>::begin<boost::shared_ptr<int>, std::allocator<int> >()’ from ‘std::vector<boost::shared_ptr<int>, std::allocator<int> >::const_iterator {aka __gnu_cxx::__normal_iterator<const boost::shared_ptr<int>*, std::vector<boost::shared_ptr<int>, std::allocator<int> > >}’ to ‘std::vector<boost::shared_ptr<const int>, std::allocator<int> >::const_iterator {aka __gnu_cxx::__normal_iterator<const boost::shared_ptr<const int>*, std::vector<boost::shared_ptr<const int>, std::allocator<int> > >}’
         return _v.begin ();

I suspect all the other containers in boost::container suffer from the same problem, e.g. they allow compilation of something wrong.

Incidentally, it would be nice to have a way to rebind iterators for smart pointers, so that full encapsulation can be achieved also for const accessor methods.

Change History (2)

comment:1 by Eric Niebler, 8 years ago

Says Ion Gaztañaga:

It's a bug. The following line in class vec_iterator (vector.hpp):

typedef typename

boost::intrusive::pointer_traits<Pointer>

ptr_traits;

should be pointer_traits<pointer> ("pointer" in lowercase).

comment:2 by Ion Gaztañaga, 8 years ago

Resolution: fixed
Status: newclosed

Thanks for the report. Fixed in:

https://github.com/boostorg/container/commit/ad54608d780f489d3e3119a097046da4fc23ce4e

Hopefully in time for Boost 1.58

Note: See TracTickets for help on using tickets.