[Development] QList

Marc Mutz marc.mutz at kdab.com
Sat Mar 25 18:22:22 CET 2017


On Saturday 25 March 2017 16:29:09 Thiago Macieira wrote:
> On sábado, 25 de março de 2017 00:57:57 PDT Marc Mutz wrote:
> > On 2017-03-24 20:55, Thiago Macieira wrote:
> > > Em sexta-feira, 24 de março de 2017, às 09:18:05 PDT, Marc Mutz
> > > escreveu:
> > >> > Are you of the opinion that private inheritance has no purpose and
> > >> > should
> > >> > never be used?
> > >> 
> > >> No, and if you look at code I have written over the years, you will
> > >> see that
> > >> I do use it.
> > >> 
> > >> One thing I've looked into in the past is this: Q6Polygon should
> > >> inherit a
> > >> Q6VectorBase<QPoint> that also Q6Vector<QPoint> inherits. This will
> > >> allow
> > >> easy specialisation of QVector<T*> by inheriting QBasicVector<const
> > >> void*>.
> > > 
> > > Can you elaborate on your thinking? What's QBasicVector and what's
> > > QVector?
> > 
> > QBasicVector (or QVectorBase, or ...) is QVector with protection against
> > using it as-is (e.g. protected dtor). QVector inherits QBasicVector to
> > lift the restriction. This inheritance may even be public to avoid lots
> > of using QBasicVector::foo; This is ok, because QBasicVector is not
> > usable as-is, but I'd still make it private, because when you start to
> > inherit to specialise (QVector<T*> : QBasicVector<const void*>), you
> > don't want the void* methods to leak.
> > 
> > So, if you absolutely are set on inheritance, then use the same pattern.
> > But I don't see this here. None of the points that makes this a good
> > idea for QVector (or QVLA) pertains to QString: we don't need to fight
> > template bloat, we don't have multiple classes inheriting QStringView...
> 
> And what are the points that make QBasicVector good? If QBasicVector is not
> usable as-is, then it must be useful because it's sharing code between
> QVector and something else. What is that something else?

The main idea of QBasicVector is to fight template bloat by e.g. inheriting 
both QVector<const T> and QVector<T> from the same QBasicVector. Likewise, all 
QVector<T*> can inherit QBasicVector<const void*>. Or all QVector<T>, 
intergral_type<T> can inherit QBasicVector<QIntegerForSizeof<T>::Unsigned>, or 
all QVector<E>, is_enum<E>, from 
QBasicVector<QIntegerForSizeof<underlying_type_t<T>>::Unsigned>, or even 
QVector<T>, qt_is_refcounted<T> && sizeof(T) == sizeof(void*) : 
QBasicVector<QExplicitlySharedDataPointer<QSharedData>>...

You can do this with composition, too. You don't even need QBasicVector. But 
if you e.g. want to collapse all QV<T*> to QV<const void*>, you need to either 
manually specialise QV<const void*> or use SFINAE so the T* case does not 
match const void*. Then you can aggregate a QV<const void*> in QV<T*> and 
delegate all functions there.

With a separate QBasicVector, however, you don't need that special-casing of 
const void*, or any of the other basic instantiated types, since the type 
where you partially specialise is different from the implementation type.

You can also generally have QVector<T> _contain_ a QBasicVector<T>, and do the 
specialisation via a type trait:

  template <typename T>
  class QVector {
      using underlying_t = typename qvector_choose_underlying_for<T>::type;
      QBasicVector<underlying_t> impl;
      T* begin() { return reinterpret_cast<T*>
                   (const_cast<std::remove_cv_t<underlying_t>*>
                   (impl.begin())); }
      // ...
  }

  template <typename T>
  struct qvector_choose_underlying_for : std::add_const<T> {};
  template <typename T, std::enable_if_t<std::is_enum_v<T>, bool> = false>
  struct qvector_choose_underlying_for<E>
      : qvector_choose_underlying_for<std::underlying_t<E>> {};
  // ...

There are lots of options here, too. But if you throw extern templates into 
the mix, it probably pays off if QVector<T>::reserve() _is_ 
QBasicVector<underlying_t<T>>::reserve(), meaning inheritance, so that the 
compiler need not instantiate a delegating function but directly hits the 
extern template declaration for QBasicVector.

I have no idea how that plays with inlining, though. Maybe in the end this 
also turns into a case for using free functions to control inlining better.

I also have not thought this through.

But all that said, all this specialisation stuff, all the types that want to 
inherit QVector because they are some form of collection of things, with no or 
little constraints added, and all these not-yet-mentioned uses of QBV to 
implement fast conversion from, say, QPolygon to QVector<QPoint>, even though 
they are unrelated, these all do not apply to QString.

Thanks,
Marc

-- 
Marc Mutz <marc.mutz at kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts



More information about the Development mailing list