c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]
-
Hello, I've got a warning message in Qt Creator. I am using old version Qt libraries 5.9.5.
The compiled binary is aborted. Any idea how to solve it?/home/user/Documents/QtTest/backend/src/main.cpp:128:9: c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]
range-loop-detach Finds places where you're using C++11 range-loops with non-const Qt containers (potential detach). Fix it by marking the container const, or, since Qt 5.7, use qAsConst(): Example for (auto i : qAsConst(list)) { ... } Fixits This check supports adding missing qAsConst.
-
@JonB
I am sorry, forgot to paste the code. I don't understand how to fix it from the warning message.void Scanner::run() { qDebug() << "thread" << QThread::thread()->currentThreadId() << "started"; while (m_bRunning) { QSet<QString> arrFiles = QDir(m_strPath).entryList({"*.txt","*.dat"}).toSet(); // report all changes and store the current state for (const QString &strFile : (arrFiles - m_arrLastFiles)) { emit Change(QString(tr("File %1 was added")).arg(strFile)); } for (const QString &strFile : (m_arrLastFiles - arrFiles)) { emit Change(QString(tr("File %1 was removed")).arg(strFile)); } m_arrLastFiles = arrFiles; QThread::sleep(1); } }
Hi @opencode,
@JonB is correct, those two lines will cause unnecessary deep copies.
Unfortunately, you can't fix that warning with
qAsConst
:because it cannot be efficiently implemented for rvalues without leaving dangling references.
For this reason Qt deletes the relevant template instantiation. So, you're left with either explicitly casting the temporary to be
const
, or assign to aconst
variable.// Warning: c++11 range-loop might detach Qt container (QSet) for (const QString &strFile : (arrFiles - m_arrLastFiles)); // Error: Call to deleted function 'qAsConst' for (const QString &strFile : qAsConst(arrFiles - m_arrLastFiles)); // Compiles ok. for (const QString &strFile : static_cast<const QSet<QString>>(arrFiles - m_arrLastFiles)) // Ok. const auto set = (arrFiles - m_arrLastFiles); for (const QString &strFile : set);
Cheers.
-
Hello, I've got a warning message in Qt Creator. I am using old version Qt libraries 5.9.5.
The compiled binary is aborted. Any idea how to solve it?/home/user/Documents/QtTest/backend/src/main.cpp:128:9: c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]
range-loop-detach Finds places where you're using C++11 range-loops with non-const Qt containers (potential detach). Fix it by marking the container const, or, since Qt 5.7, use qAsConst(): Example for (auto i : qAsConst(list)) { ... } Fixits This check supports adding missing qAsConst.
@opencode said in c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]:
Any idea how to solve it?
-
Since you choose not to show your source code where it's complaining, you'd like someone to tell you what's wrong it without being allowed to see it, right?
-
To solve it change your code like the error message tells you to. It instructs you exactly what to do. That's why the message is what it is.
-
-
@opencode said in c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]:
Any idea how to solve it?
-
Since you choose not to show your source code where it's complaining, you'd like someone to tell you what's wrong it without being allowed to see it, right?
-
To solve it change your code like the error message tells you to. It instructs you exactly what to do. That's why the message is what it is.
@JonB
I am sorry, forgot to paste the code. I don't understand how to fix it from the warning message.void Scanner::run() { qDebug() << "thread" << QThread::thread()->currentThreadId() << "started"; while (m_bRunning) { QSet<QString> arrFiles = QDir(m_strPath).entryList({"*.txt","*.dat"}).toSet(); // report all changes and store the current state for (const QString &strFile : (arrFiles - m_arrLastFiles)) { emit Change(QString(tr("File %1 was added")).arg(strFile)); } for (const QString &strFile : (m_arrLastFiles - arrFiles)) { emit Change(QString(tr("File %1 was removed")).arg(strFile)); } m_arrLastFiles = arrFiles; QThread::sleep(1); } }
-
-
@JonB
I am sorry, forgot to paste the code. I don't understand how to fix it from the warning message.void Scanner::run() { qDebug() << "thread" << QThread::thread()->currentThreadId() << "started"; while (m_bRunning) { QSet<QString> arrFiles = QDir(m_strPath).entryList({"*.txt","*.dat"}).toSet(); // report all changes and store the current state for (const QString &strFile : (arrFiles - m_arrLastFiles)) { emit Change(QString(tr("File %1 was added")).arg(strFile)); } for (const QString &strFile : (m_arrLastFiles - arrFiles)) { emit Change(QString(tr("File %1 was removed")).arg(strFile)); } m_arrLastFiles = arrFiles; QThread::sleep(1); } }
@opencode
Well, not that I have tried it, but I believe it is asking you change the right-hand side range expression in your loops:for (const QString &strFile : qAsConst(arrFiles - m_arrLastFiles)) for (const QString &strFile : qAsConst(m_arrLastFiles - arrFiles))
Without the
qAsConst()
marker the danger is that those expressions will cause a "detach" --- i.e. copy --- of the elements comprising the range. This would still work, but is inefficient: the compiler knows from yourconst
iterators that you will only be reading. not altering the range elements so suggests you insert theqAsConst()
which will allow the range elements to be accessed in the original container without unnecessary copying. -
@JonB
I am sorry, forgot to paste the code. I don't understand how to fix it from the warning message.void Scanner::run() { qDebug() << "thread" << QThread::thread()->currentThreadId() << "started"; while (m_bRunning) { QSet<QString> arrFiles = QDir(m_strPath).entryList({"*.txt","*.dat"}).toSet(); // report all changes and store the current state for (const QString &strFile : (arrFiles - m_arrLastFiles)) { emit Change(QString(tr("File %1 was added")).arg(strFile)); } for (const QString &strFile : (m_arrLastFiles - arrFiles)) { emit Change(QString(tr("File %1 was removed")).arg(strFile)); } m_arrLastFiles = arrFiles; QThread::sleep(1); } }
Hi @opencode,
@JonB is correct, those two lines will cause unnecessary deep copies.
Unfortunately, you can't fix that warning with
qAsConst
:because it cannot be efficiently implemented for rvalues without leaving dangling references.
For this reason Qt deletes the relevant template instantiation. So, you're left with either explicitly casting the temporary to be
const
, or assign to aconst
variable.// Warning: c++11 range-loop might detach Qt container (QSet) for (const QString &strFile : (arrFiles - m_arrLastFiles)); // Error: Call to deleted function 'qAsConst' for (const QString &strFile : qAsConst(arrFiles - m_arrLastFiles)); // Compiles ok. for (const QString &strFile : static_cast<const QSet<QString>>(arrFiles - m_arrLastFiles)) // Ok. const auto set = (arrFiles - m_arrLastFiles); for (const QString &strFile : set);
Cheers.
-
Hi @opencode,
@JonB is correct, those two lines will cause unnecessary deep copies.
Unfortunately, you can't fix that warning with
qAsConst
:because it cannot be efficiently implemented for rvalues without leaving dangling references.
For this reason Qt deletes the relevant template instantiation. So, you're left with either explicitly casting the temporary to be
const
, or assign to aconst
variable.// Warning: c++11 range-loop might detach Qt container (QSet) for (const QString &strFile : (arrFiles - m_arrLastFiles)); // Error: Call to deleted function 'qAsConst' for (const QString &strFile : qAsConst(arrFiles - m_arrLastFiles)); // Compiles ok. for (const QString &strFile : static_cast<const QSet<QString>>(arrFiles - m_arrLastFiles)) // Ok. const auto set = (arrFiles - m_arrLastFiles); for (const QString &strFile : set);
Cheers.
@Paul-Colby
Thanks for clarifying, Paul. Shame the error message here suggests something which you cannot actually do :)I am just musing on this. If I understand correctly, the two variables in the range are
QSet
s and-
(minus) returns the difference set (first set with second set's items removed). I'm not sure how this would ever reference the original container, I would have thought it has to produce a new set anyway? -
-
Hi @opencode,
@JonB is correct, those two lines will cause unnecessary deep copies.
Unfortunately, you can't fix that warning with
qAsConst
:because it cannot be efficiently implemented for rvalues without leaving dangling references.
For this reason Qt deletes the relevant template instantiation. So, you're left with either explicitly casting the temporary to be
const
, or assign to aconst
variable.// Warning: c++11 range-loop might detach Qt container (QSet) for (const QString &strFile : (arrFiles - m_arrLastFiles)); // Error: Call to deleted function 'qAsConst' for (const QString &strFile : qAsConst(arrFiles - m_arrLastFiles)); // Compiles ok. for (const QString &strFile : static_cast<const QSet<QString>>(arrFiles - m_arrLastFiles)) // Ok. const auto set = (arrFiles - m_arrLastFiles); for (const QString &strFile : set);
Cheers.
@Paul-Colby The warnning message in Qt Creator desapeared. Thank you. :)
-
@Paul-Colby
Thanks for clarifying, Paul. Shame the error message here suggests something which you cannot actually do :)I am just musing on this. If I understand correctly, the two variables in the range are
QSet
s and-
(minus) returns the difference set (first set with second set's items removed). I'm not sure how this would ever reference the original container, I would have thought it has to produce a new set anyway?Hi @JonB,
I would have thought it has to produce a new set anyway?
That's correct, the compiler creates a temporary
QSet
, and that's thatQSet
that the warning is referring to.Consider this pseudo-code:
for (const auto &value: (QSet<T> temporarySet = (setA - setB)));
When the compiler says "range-loop might detach Qt container (QSet)", the
QSet
it's referring to is thetemporarySet
, not the variablesetA
orsetB
.QSet::operator-() returns a non-const
QSet<T>
, so even ifsetA
andsetB
are bothconst
, the temporary rvaluetemporarySet
is non-const, so the compiler picks the non-constQSet::begin()
overload and Qt detaches to ensure the returnedtemporarySet
(non-const) iterator can't accidentally modifysetA
within the loop.By simply assigning the temporary set to a
const
variable first, or casting to addconst
, the compiler is forced to use theQSet::begin() const
overload instead, which returns aQSet::const_iterator
, guaranteeing that the set won't be modified inside the loop (not by the iterator, anyway), so Qt doesn't need to detach (can continue implicitly sharing the underlying data).I hope that explains it a little better :)
Cheers.
-
Hi @JonB,
I would have thought it has to produce a new set anyway?
That's correct, the compiler creates a temporary
QSet
, and that's thatQSet
that the warning is referring to.Consider this pseudo-code:
for (const auto &value: (QSet<T> temporarySet = (setA - setB)));
When the compiler says "range-loop might detach Qt container (QSet)", the
QSet
it's referring to is thetemporarySet
, not the variablesetA
orsetB
.QSet::operator-() returns a non-const
QSet<T>
, so even ifsetA
andsetB
are bothconst
, the temporary rvaluetemporarySet
is non-const, so the compiler picks the non-constQSet::begin()
overload and Qt detaches to ensure the returnedtemporarySet
(non-const) iterator can't accidentally modifysetA
within the loop.By simply assigning the temporary set to a
const
variable first, or casting to addconst
, the compiler is forced to use theQSet::begin() const
overload instead, which returns aQSet::const_iterator
, guaranteeing that the set won't be modified inside the loop (not by the iterator, anyway), so Qt doesn't need to detach (can continue implicitly sharing the underlying data).I hope that explains it a little better :)
Cheers.
@Paul-Colby said in c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]:
When the compiler says "range-loop might detach Qt container (QSet)", the QSet it's referring to is the temporarySet, not the variable setA or setB.
Yes, this is what I thought.
It would be interesting to see (I have not tried it) whether the end result is that the compiler actually generates any different code for
const auto set = (arrFiles - m_arrLastFiles);
vsauto set = (arrFiles - m_arrLastFiles);
followed by thefor
loop in this case :) -
@Paul-Colby said in c++11 range-loop might detach Qt container (QSet) [clazy-range-loop-detach]:
When the compiler says "range-loop might detach Qt container (QSet)", the QSet it's referring to is the temporarySet, not the variable setA or setB.
Yes, this is what I thought.
It would be interesting to see (I have not tried it) whether the end result is that the compiler actually generates any different code for
const auto set = (arrFiles - m_arrLastFiles);
vsauto set = (arrFiles - m_arrLastFiles);
followed by thefor
loop in this case :) -
@JonB Hi John, how do I find generated code in Qt Creator by compiler? I have no idea. The variable const auto set = (arrFiles - m_arrLastFiles); helped to solve the issue.