Marc Mutz wrote: > On Friday 15 January 2016 03:58:12 Kevin Kofler wrote: >> So why not just add a QOptional that works the same as std::optional? > > a) because we can't (we don't yet require the necessary language features)
A QOptional<T> that works with Qt's implicitly-shared data objects (such as QStringList, which is what it is wanted for here) only really needs this: T data; bool present; (I guess that order will give the better memory layout than the opposite order.) If present is false, you just put a default-constructed T into the (ignored) data field, which will simply have a null d-pointer and thus cost you almost no time to construct. I don't see what further language features are needed for us. > b) because once introduced, people will add all kinds of Qt-specific stuff > to it, making it impossible to replace it with std::optional down the > road. And since it will be good enough for the low demands of Qt > development, it will no longer see development and fall behind the state > of the art. So what? Qt users are not forced to use it, and I'm sure several will, maybe BECAUSE of the "all kinds of Qt-specific stuff" that people actually find convenient. > <rant> > Consider QVector: it has been Qt-ifed by both adding (technically) useless > duplicate Qt-ish API, CoW, and a Qt-specific type classification scheme > plus corresponding optimisations that make it very hard to argue for > std::vector use instead. The Qt community had two decades where they could > have influenced the direction std::vector would take so it could allow the > same optimisations that QVector has, but the time and energy was instead > put into re-writing the containers for every major release (yes, for Qt 5, > too, and Thiago's Qt 6 QVector again looks much different from the Qt 5 > one). But the Qt-ish API and the CoW are exactly what makes the Qt containers NICE to use, unlike the STL. I have found myself more than once using QtCore in a project solely and explicitly for the container classes! They are what makes C++ a nice-to-use language. > The CoW makes QVector slow and increase code size, leads to hidden > detaches that not even Qt experts regularly avoid in their daily work, and Well, I am well aware of the issue, and know to use e.g.: static_cast<const QVector &>(vec).first() when needed. (I guess this is actually more efficient than the popular .at(0) workaround. It is definitely not less efficient.) But normally I will just always operate on const data structures (usually const references) when I'm not writing to them, so I don't have to cast anything. (And of course, when I'm writing to them, chances are the detach is exactly what I want or need.) > doesn't even work properly because you can take iterators into a QVector > and, after copying the vector, change both copies through the iterator > into the first. That is a conscious trade-off because making the container > unsharable, as it must become once it hands out iterators, would make CoW > fail to take effect _all the time_. But it leads to careless copying of > QVectors that also doesn't really help with porting to std::vector. So don't use iterators into QVector, use indexes, it's a random-access container. The non-const operator[] that you use then DOES detach when needed. (And of course, for read-only iteration, foreach works great.) The only container type where I found iterators to be truly useful is maps (QMap, QHash, etc.), where the iterator can give me both key and value at once and saves me the key lookup. > All the while - and I don't believe I'm saying this - std::vector *blazes > the trail* with move semantics, emplace_back and stateful allocators > (making QVarLengthArray all but useless). Does QVector use move semantics? > No. Move semantics are mainly an ugly way to avoid copies if you don't have CoW. With CoW data structures, all you save through move semantics is the reference counting. And move semantics make it easy to shoot yourself in the foot. (Either you leave behind an invitation for a use-after-free bug, or you end up swapping instead of assigning, which is also a pessimization.) > Does QVector have emplace_back? No. Just like the move semantics, this is also an ugly and complicated way to avoid a copy if you don't have CoW or merely save the reference counting if you do. > Does QVector have an allocator, even one that, citing Alexandrescu, > actually deals with allocation? No. For the average programmer, an allocator is just an obscure thing that shows up as a template parameter in all the error messages making them ugly and unreadable. The fact that Qt containers only have the template arguments that are intuitively templated on (e.g., QVector has only the contained type as a template parameter) is really a feature, not a bug. > Does QVector, even with Q_PRIMITIVE_TYPE payloads, expand to less code as > std::vector? No: https://codereview.qt-project.org/145587 (comment from > 01-14 11:21). So there is room for improvement there. But in the end, this is not going to be the deciding factor for which implementation to pick for most programmers. And in the end, you need to compare QVector with class libraries in programming languages that offer comparable convenience, not with the C++ STL. If you try to force programmers to use the STL, chances are they will rather just switch to some other programming language that offers the semantics they expect. > Does QVector have a debug mode comparable to that of std::vector > implementations? Nope. I never had any need for that. > Or: The only reason I ever used std::list was to use its splice() feature. > Does QLinkedList have splice()? No, of course not. Because it _cannot_ > (it's CoWed: how do you take ownership of nodes if the they are shared? By > copying all other nodes in a detach()?). Then use std::list if it suits your use case better. Just don't force everyone else to use it. Personally, I also don't normally have a use for QLinkedList, QList suits my needs just fine. :-p (In fact, QList was changed from the linked list it was in Qt 3 to an array of pointers in Qt 4 exactly BECAUSE that's the more efficient data structure in most practical applications.) And I simply find the Qt containers to be much nicer to work with as a whole. In fact, I recently had to use std::priority_queue for a (QtCore- based) project of mine, a container class that sadly is not implemented in Qt (*). After attempting to use it directly and cursing about it, I ended up implementing a Qt-style wrapper around it: * My data class simply multiple-inherits from public QSharedData, public std::priority_queue<T>. * My public class contains a QSharedDataPointer of the above, renames the methods to names matching QQueue, and in particular adds a dequeue() method that does both top() and pop(). Null objects (null d-pointer) are also handled gracefully (the const methods fake an empty queue, the enqueue (= STL push) method allocates the d-pointer). So, a few lines of boilerplate, and suddenly the API becomes usable, and CoW just works. Incidentally, the CoW was also the only way I found to avoid a copy of the whole queue while initializing while keeping C++98 compatibility (so no move or swap): * I need to initialize my priority_queue from a list (actually a QList :-) but I would have the same issue with QVector or even std::vector). * There is no push method that takes a whole list of items. * So I can only: - either push every item one at a time, which sorts them less efficiently than a bulk insert, - or use the constructor that takes iterators, which then leaves me with a whole std::priority_queue to copy. My CoW wrapper avoids that copy. * And my API wrapper actually accepts the QList (or actually any list type with constBegin() and constEnd() methods) directly instead of requiring iterators. (*) I did find one third-party QPriorityQueue class, but it was just a wrapper around std::priority_queue that was neither CoW nor had API consistency with QQueue, so it was better to implement my own. > This is why we need to stop duplicating std API. It's not Qt's core > competency to meddle with things we only have a half-interest in. It's fun > to write QOptional. Until it isn't anymore. And then the (Qt) world needs > to live with the poor-man's std::optional for the next few decades. I think having more Qt containers would actually be a good thing, not something to be scared of. Kevin Kofler _______________________________________________ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development