diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86c22859cf826535f46302ea52500fcd9205f1d3..bbf0eaa56f1f0be4e133b921253f4b8cf1f3b5ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
 
 # Master (will become release 2.10)
 
+- Base the implementation of `VariableBlockVector` on `std::vector` as the storage type. Note that
+  this prevents from using `bool` as block type that was possible before.
+
 - A method `BCRSMatrix::setIndicesNoSort()` was added. Similar
   to `BCRSMatrix::setIndices()` this allows to insert all indices
   of a row at once, but - incontrast to the latter - does not sort them.
diff --git a/dune/istl/test/vbvectortest.cc b/dune/istl/test/vbvectortest.cc
index 2ecd29cf9b38269e08dbb1f0196404c4f017b7bc..89e563f82617f4de5b010c204b3119d3a5e09665 100644
--- a/dune/istl/test/vbvectortest.cc
+++ b/dune/istl/test/vbvectortest.cc
@@ -87,5 +87,10 @@ int main()
   testVectorSpaceOperations(v5);
   testScalarProduct(v5);
 
+  // check move construction
+  auto v6 = std::move(v4);
+  suite.check(v6.size()==20, "Check size of moved-constructed object");
+  suite.check(v6[0].size() == 8, "Check if blocksize of move constructed vector survived");
+
   return suite.exit();
 }
diff --git a/dune/istl/vbvector.hh b/dune/istl/vbvector.hh
index 681df8c85e6ed24ce8a58dff2919edda5cb85678..8ca5bcae75ffc17a6cace8a5695792d3b444fedb 100644
--- a/dune/istl/vbvector.hh
+++ b/dune/istl/vbvector.hh
@@ -12,6 +12,7 @@
 #include <memory>
 
 #include <dune/common/ftraits.hh>
+#include <dune/common/indexediterator.hh>
 #include <dune/common/iteratorfacades.hh>
 #include "istlexception.hh"
 #include "bvector.hh"
@@ -45,8 +46,16 @@ namespace Dune {
                               // on the large array. However, access operators have to be
                               // overwritten.
   {
+    using Base = Imp::block_vector_unmanaged<B,typename A::size_type>;
+
     // just a shorthand
-    typedef Imp::BlockVectorWindow<B,A> window_type;
+    using window_type = Imp::BlockVectorWindow<B,A>;
+
+    // data-structure holding the windows (but not the actual data)
+    using VectorWindows = std::vector<window_type, typename std::allocator_traits<A>::template rebind_alloc<window_type>>;
+
+    // block type bool is not supported since std::vector<bool> is used for storage
+    static_assert(not std::is_same_v<B,bool>, "Block type 'bool' not supported by VariableBlockVector.");
 
   public:
 
@@ -56,338 +65,166 @@ namespace Dune {
     using field_type = typename Imp::BlockTraits<B>::field_type;
 
     //! export the allocator type
-    typedef A allocator_type;
+    using allocator_type = A;
 
     /** \brief Export type used for references to container entries
      *
      * \note This is not B&, but an internal proxy class!
      */
-    typedef window_type& reference;
+    using reference = window_type&;
 
     /** \brief Export type used for const references to container entries
      *
      * \note This is not B&, but an internal proxy class!
      */
-    typedef const window_type& const_reference;
+    using const_reference = const window_type&;
 
     //! The size type for the index access
-    typedef typename A::size_type size_type;
+    using size_type = typename A::size_type;
 
     /** \brief Type of the elements of the outer vector, i.e., dynamic vectors of B
      *
      * Note that this is *not* the type referred to by the iterators and random access operators,
      * which return proxy objects.
      */
-    typedef BlockVector<B,A> value_type;
+    using value_type = BlockVector<B,A>;
 
     /** \brief Same as value_type, here for historical reasons
      */
-    typedef BlockVector<B,A> block_type;
+    using block_type = BlockVector<B,A>;
 
     //===== constructors and such
 
-    /** constructor without arguments makes empty vector,
-            object cannot be used yet
+    /**
+     * \brief Constructor without arguments makes an empty vector.
+     * \note object cannot be used yet. The size and block sizes need to be initialized.
      */
-    VariableBlockVector () : Imp::block_vector_unmanaged<B,size_type>()
-    {
-      // nothing is known ...
-      nblocks = 0;
-      block = nullptr;
-      initialized = false;
-    }
-
-    /** make vector with given number of blocks, but size of each block is not yet known,
-            object cannot be used yet
+    VariableBlockVector () :
+      Base()
+    {}
+
+    /**
+     * \brief Construct a vector with given number of blocks, but size of each
+     * block is not yet known.
+     * \note Object cannot be used yet. Needs to be initialized using the
+     * `createbegin()` and `createend()` create-iterators to fill the block sizes.
      */
-    explicit VariableBlockVector (size_type _nblocks) : Imp::block_vector_unmanaged<B,size_type>()
-    {
-      // we can allocate the windows now
-      nblocks = _nblocks;
-      if (nblocks>0)
-      {
-        block = windowAllocator_.allocate(nblocks);
-        new (block) window_type[nblocks];
-      }
-      else
-      {
-        nblocks = 0;
-        block = nullptr;
-      }
-
-      // Note: memory in base class still not allocated
-      // the vector not usable
-      initialized = false;
-    }
-
-    /** make vector with given number of blocks each having a constant size,
-            object is fully usable then.
-
-            \param _nblocks Number of blocks
-            \param m Number of elements in each block
+    explicit VariableBlockVector (size_type numBlocks) :
+      Base(),
+      block(numBlocks)
+    {}
+
+    /**
+     * \brief Construct a vector with given number of blocks each having a
+     * constant size.
+     * \note Object is fully usable after construction.
+     *
+     * \param numBlocks Number of blocks
+     * \param blockSize Number of elements in each block
      */
-    VariableBlockVector (size_type _nblocks, size_type m) : Imp::block_vector_unmanaged<B,size_type>()
+    VariableBlockVector (size_type numBlocks, size_type blockSize) :
+      Base(),
+      block(numBlocks),
+      storage_(numBlocks*blockSize)
     {
       // and we can allocate the big array in the base class
-      this->n = _nblocks*m;
-      if (this->n>0)
-      {
-        this->p = allocator_.allocate(this->n);
-        new (this->p)B[this->n];
-      }
-      else
-      {
-        this->n = 0;
-        this->p = nullptr;
-      }
+      syncBaseArray();
 
-      // we can allocate the windows now
-      nblocks = _nblocks;
-      if (nblocks>0)
-      {
-        // allocate and construct the windows
-        block = windowAllocator_.allocate(nblocks);
-        new (block) window_type[nblocks];
-
-        // set the windows into the big array
-        for (size_type i=0; i<nblocks; ++i)
-          block[i].set(m,this->p+(i*m));
-      }
-      else
-      {
-        nblocks = 0;
-        block = nullptr;
-      }
+      // set the windows into the big array
+      for (size_type i=0; i<numBlocks; ++i)
+        block[i].set(blockSize,this->p+(i*blockSize));
 
       // and the vector is usable
       initialized = true;
     }
 
-    //! copy constructor, has copy semantics
-    VariableBlockVector (const VariableBlockVector& a)
+    //! Copy constructor, has copy semantics
+    VariableBlockVector (const VariableBlockVector& a) :
+      Base(static_cast<const Base&>(a)),
+      block(a.block),
+      storage_(a.storage_)
     {
-      // allocate the big array in the base class
-      this->n = a.n;
-      if (this->n>0)
-      {
-        // allocate and construct objects
-        this->p = allocator_.allocate(this->n);
-        new (this->p)B[this->n];
+      syncBaseArray();
 
-        // copy data
-        for (size_type i=0; i<this->n; i++) this->p[i]=a.p[i];
-      }
-      else
-      {
-        this->n = 0;
-        this->p = nullptr;
-      }
-
-      // we can allocate the windows now
-      nblocks = a.nblocks;
-      if (nblocks>0)
-      {
-        // alloc
-        block = windowAllocator_.allocate(nblocks);
-        new (block) window_type[nblocks];
-
-        // and we must set the windows
-        block[0].set(a.block[0].getsize(),this->p);           // first block
-        for (size_type i=1; i<nblocks; ++i)                         // and the rest
-          block[i].set(a.block[i].getsize(),block[i-1].getptr()+block[i-1].getsize());
-      }
-      else
-      {
-        nblocks = 0;
-        block = nullptr;
+      // and we must set the windows
+      if (block.size()>0) {
+        block[0].set(block[0].getsize(),this->p);           // first block
+        for (size_type i=1; i<block.size(); ++i)                         // and the rest
+          block[i].set(block[i].getsize(),block[i-1].getptr()+block[i-1].getsize());
       }
 
       // and we have a usable vector
-      initialized = true;
+      initialized = a.initialized;
     }
 
-    //! free dynamic memory
-    ~VariableBlockVector ()
+    //! Move constructor:
+    VariableBlockVector (VariableBlockVector&& tmp) :
+      Base()
     {
-      if (this->n>0) {
-        size_type i=this->n;
-        while (i)
-          this->p[--i].~B();
-        allocator_.deallocate(this->p,this->n);
-      }
-      if (nblocks>0) {
-        size_type i=nblocks;
-        while (i)
-          block[--i].~window_type();
-        windowAllocator_.deallocate(block,nblocks);
-      }
+      tmp.swap(*this);
+    }
+
+    ~VariableBlockVector () = default;
+
 
+    //! Copy and move assignment
+    VariableBlockVector& operator= (VariableBlockVector tmp)
+    {
+      tmp.swap(*this);
+      return *this;
+    }
+
+    //! Exchange the storage and internal state with `other`.
+    void swap (VariableBlockVector& other) noexcept
+    {
+      using std::swap;
+      swap(storage_, other.storage_);
+      swap(block, other.block);
+      swap(initialized, other.initialized);
+
+      other.syncBaseArray();
+      syncBaseArray();
     }
 
+    //! Free function to swap the storage and internal state of `lhs` with `rhs`.
+    friend void swap (VariableBlockVector& lhs, VariableBlockVector& rhs) noexcept
+    {
+      lhs.swap(rhs);
+    }
 
     //! same effect as constructor with same argument
-    void resize (size_type _nblocks)
+    void resize (size_type numBlocks)
     {
-      // deconstruct objects and deallocate memory if necessary
-      if (this->n>0) {
-        size_type i=this->n;
-        while (i)
-          this->p[--i].~B();
-        allocator_.deallocate(this->p,this->n);
-      }
-      if (nblocks>0) {
-        size_type i=nblocks;
-        while (i)
-          block[--i].~window_type();
-        windowAllocator_.deallocate(block,nblocks);
-      }
-      this->n = 0;
-      this->p = nullptr;
+      storage_.clear();
+
+      syncBaseArray();
 
       // we can allocate the windows now
-      nblocks = _nblocks;
-      if (nblocks>0)
-      {
-        block = windowAllocator_.allocate(nblocks);
-        new (block) window_type[nblocks];
-      }
-      else
-      {
-        nblocks = 0;
-        block = nullptr;
-      }
+      block.resize(numBlocks);
 
       // and the vector not fully usable
       initialized = false;
     }
 
     //! same effect as constructor with same argument
-    void resize (size_type _nblocks, size_type m)
+    void resize (size_type numBlocks, size_type blockSize)
     {
-      // deconstruct objects and deallocate memory if necessary
-      if (this->n>0) {
-        size_type i=this->n;
-        while (i)
-          this->p[--i].~B();
-        allocator_.deallocate(this->p,this->n);
-      }
-      if (nblocks>0) {
-        size_type i=nblocks;
-        while (i)
-          block[--i].~window_type();
-        windowAllocator_.deallocate(block,nblocks);
-      }
-
       // and we can allocate the big array in the base class
-      this->n = _nblocks*m;
-      if (this->n>0)
-      {
-        this->p = allocator_.allocate(this->n);
-        new (this->p)B[this->n];
-      }
-      else
-      {
-        this->n = 0;
-        this->p = nullptr;
-      }
-
-      // we can allocate the windows now
-      nblocks = _nblocks;
-      if (nblocks>0)
-      {
-        // allocate and construct objects
-        block = windowAllocator_.allocate(nblocks);
-        new (block) window_type[nblocks];
+      storage_.resize(numBlocks*blockSize);
+      block.resize(numBlocks);
+      syncBaseArray();
 
-        // set the windows into the big array
-        for (size_type i=0; i<nblocks; ++i)
-          block[i].set(m,this->p+(i*m));
-      }
-      else
-      {
-        nblocks = 0;
-        block = nullptr;
-      }
+      // set the windows into the big array
+      for (size_type i=0; i<block.size(); ++i)
+        block[i].set(blockSize,this->p+(i*blockSize));
 
       // and the vector is usable
       initialized = true;
     }
 
-    //! assignment
-    VariableBlockVector& operator= (const VariableBlockVector& a)
-    {
-      if (&a!=this)     // check if this and a are different objects
-      {
-        // reallocate arrays if necessary
-        // Note: still the block sizes may vary !
-        if (this->n!=a.n || nblocks!=a.nblocks)
-        {
-          // deconstruct objects and deallocate memory if necessary
-          if (this->n>0) {
-            size_type i=this->n;
-            while (i)
-              this->p[--i].~B();
-            allocator_.deallocate(this->p,this->n);
-          }
-          if (nblocks>0) {
-            size_type i=nblocks;
-            while (i)
-              block[--i].~window_type();
-            windowAllocator_.deallocate(block,nblocks);
-          }
-
-          // allocate the big array in the base class
-          this->n = a.n;
-          if (this->n>0)
-          {
-            // allocate and construct objects
-            this->p = allocator_.allocate(this->n);
-            new (this->p)B[this->n];
-          }
-          else
-          {
-            this->n = 0;
-            this->p = nullptr;
-          }
-
-          // we can allocate the windows now
-          nblocks = a.nblocks;
-          if (nblocks>0)
-          {
-            // alloc
-            block = windowAllocator_.allocate(nblocks);
-            new (block) window_type[nblocks];
-          }
-          else
-          {
-            nblocks = 0;
-            block = nullptr;
-          }
-        }
-
-        // copy block structure, might be different although
-        // sizes are the same !
-        if (nblocks>0)
-        {
-          block[0].set(a.block[0].getsize(),this->p);                 // first block
-          for (size_type i=1; i<nblocks; ++i)                               // and the rest
-            block[i].set(a.block[i].getsize(),block[i-1].getptr()+block[i-1].getsize());
-        }
-
-        // and copy the data
-        for (size_type i=0; i<this->n; i++) this->p[i]=a.p[i];
-      }
-
-      // and we have a usable vector
-      initialized = true;
-
-      return *this;     // Return reference to make constructions like a=b=c; work.
-    }
-
-
     //===== assignment from scalar
 
-    //! assign from scalar
+    //! Set all entries to the given scalar `k`.
     VariableBlockVector& operator= (const field_type& k)
     {
       (static_cast<Imp::block_vector_unmanaged<B,size_type>&>(*this)) = k;
@@ -409,12 +246,12 @@ namespace Dune {
     struct SizeProxy
     {
 
-      operator size_type() const
+      operator size_type () const
       {
         return target->getsize();
       }
 
-      SizeProxy& operator=(size_type size)
+      SizeProxy& operator= (size_type size)
       {
         target->setsize(size);
         return *this;
@@ -424,8 +261,8 @@ namespace Dune {
 
       friend class CreateIterator;
 
-      SizeProxy(window_type& t)
-        : target(&t)
+      SizeProxy (window_type& t) :
+        target(&t)
       {}
 
       window_type* target;
@@ -461,15 +298,17 @@ namespace Dune {
       CreateIterator (VariableBlockVector& _v, int _i, bool _isEnd) :
         v(_v),
         i(_i),
-        isEnd(_isEnd) {}
+        isEnd(_isEnd)
+      {}
 
-      ~CreateIterator() {
+      ~CreateIterator ()
+      {
         // When the iterator gets destructed, we allocate the memory
         // for the VariableBlockVector if
         // 1. the current iterator was not created as enditerator
         // 2. we're at the last block
         // 3. the vector hasn't been initialized earlier
-        if (not isEnd && i==v.nblocks && not v.initialized)
+        if (not isEnd && i==v.block.size() && not v.initialized)
           v.allocate();
       }
 
@@ -540,13 +379,13 @@ namespace Dune {
 #ifdef DUNE_ISTL_WITH_CHECKING
       if (initialized) DUNE_THROW(ISTLError,"no CreateIterator in initialized state");
 #endif
-      return CreateIterator(*this,0, false);
+      return CreateIterator(*this, 0, false);
     }
 
     //! get create iterator pointing to one after the last block
     CreateIterator createend ()
     {
-      return CreateIterator(*this,nblocks, true);
+      return CreateIterator(*this, block.size(), true);
     }
 
 
@@ -558,7 +397,7 @@ namespace Dune {
     window_type& operator[] (size_type i)
     {
 #ifdef DUNE_ISTL_WITH_CHECKING
-      if (i>=nblocks) DUNE_THROW(ISTLError,"index out of range");
+      if (i>=block.size()) DUNE_THROW(ISTLError,"index out of range");
 #endif
       return block[i];
     }
@@ -567,113 +406,44 @@ namespace Dune {
     const window_type& operator[] (size_type i) const
     {
 #ifdef DUNE_ISTL_WITH_CHECKING
-      if (i<0 || i>=nblocks) DUNE_THROW(ISTLError,"index out of range");
+      if (i<0 || i>=block.size()) DUNE_THROW(ISTLError,"index out of range");
 #endif
       return block[i];
     }
 
-    //! Iterator class for sequential access
-    template <class T, class R>
-    class RealIterator
-    : public RandomAccessIteratorFacade<RealIterator<T,R>, T, R>
-    {
-    public:
-      //! constructor, no arguments
-      RealIterator ()
-      {
-        p = nullptr;
-        i = 0;
-      }
-
-      //! constructor
-      RealIterator (window_type* _p, size_type _i)
-      : p(_p), i(_i)
-      {}
-
-      //! prefix increment
-      void increment()
-      {
-        ++i;
-      }
-
-      //! prefix decrement
-      void decrement()
-      {
-        --i;
-      }
-
-      //! equality
-      bool equals (const RealIterator& it) const
-      {
-        return (p+i)==(it.p+it.i);
-      }
-
-      //! dereferencing
-      window_type& dereference () const
-      {
-        return p[i];
-      }
-
-      void advance(std::ptrdiff_t d)
-      {
-        i+=d;
-      }
-
-      std::ptrdiff_t distanceTo(const RealIterator& o) const
-      {
-        return o.i-i;
-      }
-
-      // Needed for operator[] of the iterator
-      window_type& elementAt (std::ptrdiff_t offset) const
-      {
-        return p[i+offset];
-      }
-
-      /** \brief Return the index of the entry this iterator is pointing to */
-      size_type index() const
-      {
-        return i;
-      }
-
-    private:
-      window_type* p;
-      size_type i;
-    };
-
-    using Iterator = RealIterator<value_type,window_type&>;
+    using Iterator = IndexedIterator<typename VectorWindows::iterator>;
 
     //! begin Iterator
     Iterator begin ()
     {
-      return Iterator(block,0);
+      return Iterator{block.begin()};
     }
 
     //! end Iterator
     Iterator end ()
     {
-      return Iterator(block,nblocks);
+      return Iterator{block.end()};
     }
 
     //! @returns an iterator that is positioned before
     //! the end iterator of the vector, i.e. at the last entry.
     Iterator beforeEnd ()
     {
-      return Iterator(block,nblocks-1);
+      return Iterator{--block.end()};
     }
 
     //! @returns an iterator that is positioned before
     //! the first entry of the vector.
-    Iterator beforeBegin () const
+    Iterator beforeBegin ()
     {
-      return Iterator(block,-1);
+      return Iterator{--block.begin()};
     }
 
     /** \brief Export the iterator type using std naming rules */
     using iterator = Iterator;
 
     /** \brief Const iterator */
-    using ConstIterator = RealIterator<const value_type, const window_type&>;
+    using ConstIterator = IndexedIterator<typename VectorWindows::const_iterator>;
 
     /** \brief Export the const iterator type using std naming rules */
     using const_iterator = ConstIterator;
@@ -681,98 +451,102 @@ namespace Dune {
     //! begin ConstIterator
     ConstIterator begin () const
     {
-      return ConstIterator(block,0);
+      return ConstIterator{block.begin()};
     }
 
     //! end ConstIterator
     ConstIterator end () const
     {
-      return ConstIterator(block,nblocks);
+      return ConstIterator{block.end()};
     }
 
     //! @returns an iterator that is positioned before
     //! the end iterator of the vector. i.e. at the last element.
-    ConstIterator beforeEnd() const
+    ConstIterator beforeEnd () const
     {
-      return ConstIterator(block,nblocks-1);
+      return ConstIterator{--block.end()};
+    }
+
+    //! @returns an iterator that is positioned before
+    //! the first entry of the vector.
+    ConstIterator beforeBegin () const
+    {
+      return ConstIterator{--block.begin()};
     }
 
     //! end ConstIterator
     ConstIterator rend () const
     {
-      return ConstIterator(block,-1);
+      return ConstIterator{block.rend()};
     }
 
     //! random access returning iterator (end if not contained)
     Iterator find (size_type i)
     {
-      return Iterator(block,std::min(i,nblocks));
+      Iterator tmp = block.begin();
+      tmp+=std::min(i, block.size());
+      return tmp;
     }
 
     //! random access returning iterator (end if not contained)
     ConstIterator find (size_type i) const
     {
-      return ConstIterator(block,std::min(i,nblocks));
+      ConstIterator tmp = block.begin();
+      tmp+=std::min(i, block.size());
+      return tmp;
     }
 
     //===== sizes
 
     //! number of blocks in the vector (are of variable size here)
-    size_type N () const
+    size_type N () const noexcept
     {
-      return nblocks;
+      return block.size();
     }
 
     /** Number of blocks in the vector
      *
      * Returns the same value as method N(), because the vector is dense
     */
-    size_type size () const
+    size_type size () const noexcept
     {
-      return nblocks;
+      return block.size();
     }
 
 
   private:
 
-    void allocate() {
+    void allocate ()
+    {
       if (this->initialized)
         DUNE_THROW(ISTLError, "Attempt to re-allocate already initialized VariableBlockVector");
 
       // calculate space needed:
-      this->n=0;
-      for(size_type i = 0; i < nblocks; i++) {
-        this->n += block[i].size();
-      }
+      size_type storageNeeded = 0;
+      for(size_type i = 0; i < block.size(); i++)
+        storageNeeded += block[i].size();
 
-      // now we can allocate the big array in the base class of v
-      if (this->n>0)
-      {
-        // allocate and construct objects
-        this->p = allocator_.allocate(this->n);
-        new (this->p)B[this->n];
-      }
-      else
-      {
-        this->p = nullptr;
-      }
+      storage_.resize(storageNeeded);
+      syncBaseArray();
 
       // and we set the window pointers
-      this->block[0].setptr(this->p); // pointer to first block
-      for (size_type j=1; j<nblocks; ++j) // and the rest
+      block[0].setptr(this->p); // pointer to first block
+      for (size_type j=1; j<block.size(); ++j) // and the rest
         block[j].setptr(block[j-1].getptr()+block[j-1].getsize());
 
       // and the vector is ready
       this->initialized = true;
     }
 
-    size_type nblocks;            // number of blocks in vector
-    window_type* block;     // array of blocks pointing to the array in the base class
-    bool initialized;       // true if vector has been initialized
-
-    A allocator_;
+    void syncBaseArray () noexcept
+    {
+      this->p = storage_.data();
+      this->n = storage_.size();
+    }
 
-    typename std::allocator_traits<A>::template rebind_alloc<window_type> windowAllocator_;
+    VectorWindows block = {}; // vector of blocks pointing to the array in the base class
+    std::vector<B, A> storage_ = {};
+    bool initialized = false; // true if vector has been initialized
   };
 
   /** @addtogroup DenseMatVec