0

I am trying to return a rvalue reference from a function to a QVariant. It works for bool and int, but when I pass a string I get an 'invalid' QVariant.

My function:

QVariant &&AudioSettings::getValueFromTable(const QString &name)
{
    QSqlQuery query(_messengerDB);
    auto queryStr = QString("SELECT %1 FROM user_audio WHERE id = 1;").arg(name);
    if(query.exec(queryStr)){

        if(query.next()){
            qDebug() << "AudioSettings::getValueFromTable" << query.value(0);  //here I have a correct QString value => QVariant(QString, "Głośniki (USB Audio CODEC )")
            return std::move(query.value(0));

        } else {
            qDebug() << "AudioSettings::setValueToTable : No data for '" << queryStr << "'";
            return QVariant();
        }

    } else {
        qDebug()<<"AudioSettings::setValueToTable : Error during getting " << name << " form table user_audio.";
        qDebug() << query.lastQuery();
        qDebug() << query.lastError();
    }
    return QVariant();
}

when I call:

QString &&AudioSettings::getAudioOut()
{
    auto value = getValueFromTable("audio_out");  //here I get an invalid QVariant
    return value.isNull() ? QString() : value.toString(); 
}

What works is:

int AudioSettings::getRingtoneType()  //EDIT
{
    auto value = getValueFromTable("ringtone_type");  //EDIT, I am getting a QVariant with an int inside
    return value.isNull() ? 0 : value.toInt();
}

Does anyone know why?

Szpaqn
  • 545
  • 5
  • 17
  • 1
    QString when returned or passed by value does not copy its text string content. It is an implicit sharing with copy on write. Given that your attempt to 'move' QString inside of QVariant is likely messing with implicit sharing. – Alexander V Jan 19 '18 at 15:45
  • 1
    An rvalue reference is [still a reference](https://stackoverflow.com/a/1116763/2666212), the function `getValueFromTable` in your question is returning a dangling reference to a local variable and hence invoking UB... – Mike Jan 19 '18 at 15:50
  • Do you have several overloads of `getValueFromTable()` ? It looks like it. Are your sure that `getValueFromTable("audio_out")` does call `getValueFromTable(const QString&)` ? – Benjamin T Jan 19 '18 at 15:50
  • @BenjaminT `getValueFromTable(database, "ringtone_type");` just can't call `getValueFromTable(const QString &name)`. The question is missing another overload of `getValueFromTable`, and I think that this overload does not return by reference and hence it works in the question's second example... – Mike Jan 19 '18 at 15:53
  • @BenjaminT & Mike I am very sorry. I wrote the wrong function. I have corrected the code. I am sure that I invoke the correct functions, I have debuged the code with break points to do so. Could someone know where to find more information on this subject? – Szpaqn Jan 19 '18 at 18:48

1 Answers1

2

As stated in the comments, you are returning a dangling reference. So you have undefined behavior and anything can happen.

In the case that works, the QVariant holds an integer. My guess is that you are just lucky that the memory that holds the int has not be rewritten (or freed).

For the QString your luck runs out, the actual content is not stored in the QString object, but it is stored on the heap and has been released once the function has returned. All that is left is an empty/null QString.

Solution: return the QVariant by value.

Benjamin T
  • 8,120
  • 20
  • 37
  • Thank you for the answer. So I should use moving only with objects that are on the heap, not on the stack. Is that correct? – Szpaqn Jan 20 '18 at 16:12
  • @Szpaqn You can move object that are on the stack. What you cannot do is use a reference to an object that is out of scope. In particular that means that you cannot return a reference to a variable that is local to the function, because the local will be destroyed when living the function and the caller will receive a dangling reference. – Benjamin T Jan 20 '18 at 22:24