Q_INVOKABLE const getter возвращает неопределенное значение, неконстантный getter возвращает правильное значение - почему? - PullRequest
1 голос
/ 05 апреля 2020

У меня есть простой метод класса c ++:

Q_INVOKABLE const QString& getRiderTypeName(const int& riderTypeIndex) const;

с его реализацией:

const QString& CumulativeSalesZoneModel::getRiderTypeName(const int& raiderTypeIndex) const   
{
    static QString result=QString();

    if(this->cumulativeSalesZoneData!=Q_NULLPTR)
    {
        result=this->cumulativeSalesZoneData->keys().at(raiderTypeIndex);
    }   // if

    return result;
}   // getRiderTypeName

и когда вызывается из QML , он возвращает неопределенное значение . Однако, если я уменьшу оператор const , удалим ссылку и удалим stati c объявление result как часть возвращаемого значения:

Q_INVOKABLE QString getRiderTypeName(const int& riderTypeIndex) const;

и его реализация:

QString CumulativeSalesZoneModel::getRiderTypeName(const int& raiderTypeIndex) const
{
    QString result=QString();

    if(this->cumulativeSalesZoneData!=Q_NULLPTR)
    {
        result=this->cumulativeSalesZoneData->keys().at(raiderTypeIndex);
    }   // if

    return result;
}   // getRiderTypeName

Я получаю правильное значение. Почему так? Я пробовал это на нескольких Qt версиях на Windows, Linux и нескольких встроенных платах ( Raspeberry Pi , Toradex Colibri iMX6 и некоторых других). По запросу @Azeem я добавляю соответствующий QML код:

Component.onCompleted:
{
    riderTypeName=cumulativeSalesZoneModel.getRiderTypeName(riderTypesIndex);
    console.log(riderTypeName);
}   // Component.onCompleted

Консольные выходы:

qml: undefined

, если вызывается первая версия метода и выводится правильное значение, если второй версия метода называется.

1 Ответ

2 голосов
/ 05 апреля 2020

Редакция № 1 (нестатические / локальные данные, возвращаемые в виде константной ссылки):

const QString& CumulativeSalesZoneModel::getRiderTypeName( /*...*/ )
{
    QString result = QString();    // non-static/local object
    // ...
    return result;
}

Ответ № 1:

На стороне C ++ вы пытаетесь вернуть адрес (ссылку) локальной переменной, т.е. result. К тому времени, когда блок кода завершит свое выполнение, go выйдет из области видимости. Итак, вы в конечном итоге с висящей ссылкой. Использование висячей ссылки приведет к неопределенному поведению .

Проверьте ваши журналы компиляции. Вы должны получить следующее предупреждение:

предупреждение: возвращена ссылка на стековую память, связанную с локальной переменной 'result'

Вот пример программы, воспроизводящей это предупреждение: https://godbolt.org/z/pZBq7H


ПРИМЕЧАНИЕ : Следующая часть, касающаяся возврата ссылок, также применяется к ответу № 1 выше, когда код находится в Qt MO C / Сторона QML.

Редакция # 2 (stati c данные возвращаются в виде констант-ссылки):

const QString& CumulativeSalesZoneModel::getRiderTypeName( /*...*/ )
{
    static QString result = QString();    // static
    // ...
    return result;
}

Согласно Qt Мета-объектный компилятор страница, где говорится (выделено мое):

Сигналы и слоты могут иметь возвращаемые типы, но сигналы или слоты, возвращающие ссылки, будут рассматриваться как возвращающие void.

И, чтобы убедиться в этом, я создал этот небольшой test проект с несколькими опубликованными c Q_INVOKABLE методами , QML довольно прост. Возвращаемая строка отображается в виде title окна.

Вам необходимо просмотреть файлы, сгенерированные MO C.

Сгенерированный moc_test.cpp содержит эту часть:

static const uint qt_meta_data_Test[] = {

 // ... code removed for conciseness ...

 // methods: name, argc, parameters, tag, flags
       1,    0,   39,    2, 0x02 /* Public */,
       3,    0,   40,    2, 0x02 /* Public */,
       4,    0,   41,    2, 0x02 /* Public */,
       5,    0,   42,    2, 0x02 /* Public */,
       6,    0,   43,    2, 0x02 /* Public */,

 // methods: parameters
    QMetaType::QString,
    QMetaType::QString,
    QMetaType::QString,
    QMetaType::Void,
    QMetaType::Void,

       0        // eod
};

Как вы можете заметить в приведенном фрагменте кода (следуйте комментариям "методов"), существует 5 методов. В последней части только первые 3 имеют типы возвращаемых значений, т.е. QString, но последние 2 возвращают void.

И соблюдайте следующий сгенерированный метод:

void Test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Test *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: { QString _r = _t->get1_RetByValue_NonConstLocalString();
            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); }  break;
        case 1: { QString _r = _t->get2_RetByConstValue_ConstLocalString();
            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); }  break;
        case 2: { QString _r = _t->get3_RetByConstValue_StaticString();
            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r); }  break;
        case 3: _t->get4_RetByConstRef_LocalString(); break;
        case 4: _t->get5_RetByStaticConstRef_StaticString(); break;
        default: ;
        }
    }
}

Первый 3 случая в switch правильно устанавливают массив аргументов, т.е. _a, но в последних 2 случаях это не так.

Интересно отметить, что код в первых 3 действительных случаях является идентичным, за исключением вызовов функций, например:

QString _r = _t->get<...>();    // first 3 variations of get method
if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = std::move(_r);

Возвращаемый QString присваивается _r и затем ему присваивается ход _a[0] из _r.


Вы можете получить проект и запустить сеанс отладки, чтобы самостоятельно наблюдать за этим поведением. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...