Unexpected QList<QVariant> Initialiser Behaviour
-
This isn't so much a problem report, because a solution is easy to implement, but this behaviour is very unexpected and has caused me some wasted time. I post it in case it might help others to avoid it.
Initialising a member variable of type
QList<QVariant> results;using "{}" braces produces the unexpected (to me) result of creating a value of type QList<QList<QVariant>>.
Whereas initialising the member variable using "()" round brackets produces a value of the expected (to me) type QList<QVariant>.
Example code that reproduces this:class A { public: A(QList<QVariant> resultsList) : results{ resultsList } // <<<<<< { qDebug() << "A results are" << results.size() << "long"; } QList<QVariant> results; }; class B { public: B(QList<QVariant> resultsList) : results(resultsList) // <<<<<< { qDebug() << "B results are" << results.size() << "long"; } QList<QVariant> results; }; class C { public: C(QList<int> resultsList) : results{ resultsList } // <<<<<< { qDebug() << "C QList<int> results are" << results.size() << "long"; } QList<int> results; }; int main(int , char *[]) { QList<QVariant> x; x << 1 << 2 << 3; A b1{ x }; A b2(x); B c1{ x }; B c2{ x }; QList<int> i; i << 1 << 2 << 3; C a1{ i }; C a2(i); }with the output
A results are 1 long
A results are 1 long
B results are 3 long
B results are 3 long
C QList<int> results are 3 long
C QList<int> results are 3 longThe behaviour is tied to QVariant, not QList<> as the "C" class results which use a QList<int> show.
-
Can you debug the list object itself, not just the size?
In the case of A it is a nested variant list - one element which is a variant list itself. -
Sorry, I described this incorrectly.
As you say @Axel-Spoerl, case A produces a QList<QVariant>, as it should do, of course, with one element which is a QVariant<QList<QVariant>>.
This is fair enough, but tricky, I claim.
Constructing a QList<QVariant> on the stack using the same {} method produces a different result:
class A { public: A(QList<QVariant> resultsList) : results{ resultsList } // <<<<<< { qDebug() << "A results are length" << results.size(); for (auto& r : results) qDebug() << "\\ - " << r; } QList<QVariant> results; }; class B { public: B(QList<QVariant> resultsList) : results(resultsList) // <<<<<< { qDebug() << "B results are length" << results.size(); for (auto& r : results) qDebug() << "\\ - " << r; } QList<QVariant> results; }; int main(int , char *[]) { const QList<QVariant> x{ 1, 2, "3" }; A a1{ x }; B b1{ x }; QList<QVariant> C{ x }; qDebug() << "Stack C results are length" << C.size(); for (auto& r : C) qDebug() << "\\ - " << r; }A results are length 1
- QVariant(QVariantList, QList(QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")))
B results are length 3
- QVariant(int, 1)
- QVariant(int, 2)
- QVariant(QString, "3")
Stack C results are length 3
- QVariant(int, 1)
- QVariant(int, 2)
- QVariant(QString, "3") -
Sorry, I described this incorrectly.
As you say @Axel-Spoerl, case A produces a QList<QVariant>, as it should do, of course, with one element which is a QVariant<QList<QVariant>>.
This is fair enough, but tricky, I claim.
Constructing a QList<QVariant> on the stack using the same {} method produces a different result:
class A { public: A(QList<QVariant> resultsList) : results{ resultsList } // <<<<<< { qDebug() << "A results are length" << results.size(); for (auto& r : results) qDebug() << "\\ - " << r; } QList<QVariant> results; }; class B { public: B(QList<QVariant> resultsList) : results(resultsList) // <<<<<< { qDebug() << "B results are length" << results.size(); for (auto& r : results) qDebug() << "\\ - " << r; } QList<QVariant> results; }; int main(int , char *[]) { const QList<QVariant> x{ 1, 2, "3" }; A a1{ x }; B b1{ x }; QList<QVariant> C{ x }; qDebug() << "Stack C results are length" << C.size(); for (auto& r : C) qDebug() << "\\ - " << r; }A results are length 1
- QVariant(QVariantList, QList(QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")))
B results are length 3
- QVariant(int, 1)
- QVariant(int, 2)
- QVariant(QString, "3")
Stack C results are length 3
- QVariant(int, 1)
- QVariant(int, 2)
- QVariant(QString, "3")@KenAppleby-0 said in Unexpected QList<QVariant> Initialiser Behaviour:
A results are length 1
- QVariant(QVariantList, QList(QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")))
Simple:
You use X to initialize A, which uses the whole list to initalize theresultsmember which becomes a single element list ofQList<QVariant>==QVariantList.That's how I would explain this behavior.
@kshegunov made a good explanation here
-
@KenAppleby-0 said in Unexpected QList<QVariant> Initialiser Behaviour:
A results are length 1
- QVariant(QVariantList, QList(QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")))
Simple:
You use X to initialize A, which uses the whole list to initalize theresultsmember which becomes a single element list ofQList<QVariant>==QVariantList.That's how I would explain this behavior.
@kshegunov made a good explanation here
@Pl45m4 Why doesn't the same happen with the stack variable?
QList<QVariant> C{ x }; -
@Pl45m4 Why doesn't the same happen with the stack variable?
QList<QVariant> C{ x };Why doesn't the same happen with the stack variable?
It does here (Linux, GCC 13, Qt 5.15.3 or 6.7.2):
chrisw@newton:/tmp/tt$ ./tt A results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3"))) B results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") Stack C results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3"))) -
Why doesn't the same happen with the stack variable?
It does here (Linux, GCC 13, Qt 5.15.3 or 6.7.2):
chrisw@newton:/tmp/tt$ ./tt A results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3"))) B results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") Stack C results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")))I got intrigued and made some tests.
This is my output on Windows where I print the function calls from
qlist.h.
(only have an older Qt / system at hand right now)Windows 10, 5.15.2, MSVC2019
QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) called QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) called A results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3"))) \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")) B results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") Stack C results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3")I assume the problem, if any, has to do with either the
QDebugoutput (don't trust how it interprets data) or someQVariantvoodoo, as it does not happen for STL containers.Made an online test. No issues
Edit:
Maybe this sequence of output
QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) calledindicates that it encapsulates the
QVariantListsomehow and creates an "inner" list inside anotherQVariantListusing the initializer_list c'tor...@KenAppleby-0 I think this is where you are not sure whether this is the expected behavior, right?
-
I got intrigued and made some tests.
This is my output on Windows where I print the function calls from
qlist.h.
(only have an older Qt / system at hand right now)Windows 10, 5.15.2, MSVC2019
QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) called QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) called A results are length 1 \ - QVariant(QVariantList, (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3"))) \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") (QVariant(int, 1), QVariant(int, 2), QVariant(QString, "3")) B results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3") Stack C results are length 3 \ - QVariant(int, 1) \ - QVariant(int, 2) \ - QVariant(QString, "3")I assume the problem, if any, has to do with either the
QDebugoutput (don't trust how it interprets data) or someQVariantvoodoo, as it does not happen for STL containers.Made an online test. No issues
Edit:
Maybe this sequence of output
QList<class QVariant>::QList called QList<class QVariant>::QList(InputIterator) called QList<class QVariant>::QList(initializer_list) calledindicates that it encapsulates the
QVariantListsomehow and creates an "inner" list inside anotherQVariantListusing the initializer_list c'tor...@KenAppleby-0 I think this is where you are not sure whether this is the expected behavior, right?
@Pl45m4 said in Unexpected QList<QVariant> Initialiser Behaviour:
I think this is where you are not sure whether this is the expected behavior, right?
I don't know what behaviour to expect, frankly. As the behaviour is different for the stack variable case with different compilers it looks like the compiler writers weren't sure either, unless there are compiler-dependent conditionals in the QVariant code.
I have tested this on 6.7.2 with VC2019 and MinGW, with the same results: {} initialising a stack allocated QList<QVariant> behaves differently to {} initialising a QList<QVariant> member variable of a stack allocated class instance.
My only claim here is that this is tricky :-)
-
Basically the
std::initializer_listconstructor takes precedence over the copy constructor in class A (as it should by the standard) -
Basically the
std::initializer_listconstructor takes precedence over the copy constructor in class A (as it should by the standard) -
K KenAppleby 0 has marked this topic as solved on