5

I'm very confused as to why this isn't working. I must be misunderstanding something key about QVectors...

I've created an MCVE to show the issue:

#include <QCoreApplication>
#include <QVector>

struct ChunkRequest
{
    ChunkRequest(int x, int z)
    {
        this->x = x;
        this->z = z;
    }

    int x;
    int z;
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QVector<ChunkRequest> requestedChunks;

    requestedChunks.append(ChunkRequest(1, 2));

    return a.exec();
}

Compiling throws an error C2512: 'ChunkRequest' : no appropriate default constructor available

I am able to create a ChunkRequest variable with ChunkRequest req(1, 2); but as soon as I try to append it to my QVector the error throws.

I am legitimately confused as to why.

EDIT: After reading your comments, it is clear to me that QVector requires a default constructor in order to determine the size of each element in the array. But this doesn't answer why.

If a struct has a certain number of members and each member has a known size in memory (even pointers to dynamic memory are known size) then I don't see why QVector requires a default constructor? The size should be known at compile time... right?

mrg95
  • 2,371
  • 11
  • 46
  • 89
  • 1
    See also: https://stackoverflow.com/questions/33380402/what-is-the-reason-of-qvectors-requirement-for-default-constructor – MrEricSir Apr 01 '18 at 20:57
  • @MrEricSir Thanks for this! So looks like QVector requires a default constructor in order to determine size of each element in memory? So either I create a default constructor or create each element on the heap. Thanks :) – mrg95 Apr 01 '18 at 21:01
  • Indeed, yes with dynamic mem it worked! – Mohammad Kanan Apr 01 '18 at 21:03
  • Although I do find that to be an odd requirement of QVector. A struct has a set amount of members.... either direct variables or pointers to heap allocated values. So I don't understand why QVector requires a default constructor to determine size? – mrg95 Apr 01 '18 at 21:05
  • 1
    @mrg95 It's kind of buried in the docs, but [a default constructor is required](http://doc.qt.io/qt-5/containers.html#default-constructed-value). Whenever you provide a constructor in C++, it wipes out the default constructor, which means you may have to provide your own (even if it's a no-op.) It's just one of the quirks of the language. – MrEricSir Apr 01 '18 at 21:16
  • Hmmm that's a shock to me because I would assume size could be determined at compile time. What am I missing about that? – mrg95 Apr 01 '18 at 21:18
  • @mrg95 You're absolutely right about the size, it is known at compile time. What isn't known at compile time are the default values of the member variables, since primitive types such as `int` don't have a default. This is important if `QVector` were to allocate additional elements, which would call the default constructor to provide values for the member variables. – MrEricSir Apr 01 '18 at 21:31
  • Ahh. I see. I didn't know `QVector` actually created the extra elements when it allocates space. I thought it just allocated..... space. Thanks for your help :) Feel free to post this as an answer if you'd like and I will accept it. – mrg95 Apr 01 '18 at 21:33
  • So sounds like, in terms of performance, dynamic allocation would be better suited for structs? – mrg95 Apr 01 '18 at 21:40
  • It seens that Qt will always call the default constructor for small objects and then use a move operation to copy the actual object to the vector. It doesn't do it for larger objects (maybe the cost would be to high). C++ before c++11 I think also used to do that until they introduce emplace_back (instead of push_back). Maybe the reason is the same (if I remember, it can be related to cuncurrency, not sure though). – Adriel Jr Apr 01 '18 at 21:48
  • Possible duplicate of [No matching call to default constructor, when using QVector](https://stackoverflow.com/questions/54181249/no-matching-call-to-default-constructor-when-using-qvector) – ymoreau Jan 29 '19 at 12:35

2 Answers2

4

In C++ specifying any constructor for a struct (or class) instructs the compiler not to provide a default constructor automatically. It's a quirk of the language.

This becomes a problem for container classes such as QVector which can resize on the fly (either internally or explicitly.) When allocating new objects to fill the space it will call the default constructor -- but if there isn't a default constructor available a compile-time error will occur.

The problem can be solved by specifying a default constructor, even if it doesn't do anything, for example:

ChunkRequest() {}
MrEricSir
  • 8,044
  • 4
  • 30
  • 35
2

it is clear to me that QVector requires a default constructor in order to determine the size of each element in the array. But this doesn't answer why.

it has absolutely nothing to do with it, and constructors in no way determined the size of objects, their member layout does.

What mandates the default constructor is the QVector constructors and methods that allow setting a size. That constructor will be used to initialize all those elements.

Without a default constructor, the values would be uninitialized and pretty much useless garbage data.

Having a default constructor tells you "I can construct meaningful objects of that type without passing any parameters".

It doesn't matter if you personally don't use any of those constructors or methods, it is required by the class implementation nonetheless.

As of why it also uses construction for what is basically reserved memory, that would probably merit a question on its own, because at least for me there is no good reason to do that at all, it seems like a potential overhead.

dtech
  • 47,916
  • 17
  • 112
  • 190