0

在尝试QString从 a 的值构造 a 时QJsonArray,我收到以下错误:

error: passing 'const QString' as 'this' argument discards qualifiers [-fpermissive].

不知道我在这段代码中哪里弄错了:

QString <CLASS_NAME>::getData(QString callerValue) {
    QString BASE_URL = "<URL>";

    QString stringToReturn = "";
    QObject::connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply) {
      QByteArray barr = reply->readAll();
      QJsonParseError jpe;
      QJsonDocument jdoc = QJsonDocument::fromJson(barr, &jpe);
      QJsonArray synonymsArray = jdoc.array();
      foreach (const QJsonValue &jv, synonymsArray) {
        QJsonObject jo = jv.toObject();
        QString s = jo.value("<VALUE>").toString();
        stringToReturn.append(s + ", "); /* ERROR: The error above is from this line... */
      }
    }
    );
    request.setUrl(QUrl(BASE_URL + callerValue));
    manager->get(request);
    return stringToReturn;
  }
4

2 回答 2

2

这是另一个经典的“我希望世界是同步的”问题。你不能那样编码。该getData方法不能按照您希望的方式编写。getData这样做会阻塞,这非常浪费并且可能导致有趣的问题——不是最后一个是可怕的用户体验。

根据您的应用程序,将有几个可能的修复:

  1. getData使用协程以隐式延续传递风格重做co_yield- 这是未来,只能在最新的编译器上完成,除非您使用诸如 boost 协程之类的黑客攻击。

  2. 以明确的延续传递风格重做getData

  3. getData当数据可用时,以懒惰的方式重做并通知,

  4. 有一个明确的状态机来处理你的代码进度。

延续传递风格需要最少的更改。还要注意其他修复 - 最值得注意的是,您不应该使用QNetworkAccessManager' 信号:您只对这个查询的结果感兴趣,而不是每个查询!QNetworkAccessManager::finished仅当您真正拥有一个可以处理所有或至少最频繁请求的中心点时,捕获信号才有用。在这种情况下,这是一个明智的优化:malloc将第一个连接添加到一个没有连接的对象有几个 s 的开销。

void Class::getData(const QString &urlSuffix, std::function<void(const QString &)> cont) {
  auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
  QNetworkRequest request(QUrl(urlString));
  auto *reply = m_manager.get(request);
  QObject::connect(reply, &QNetworkReply::finished, this, [=]{
    QString result;
    auto data = reply->readAll();
    QJsonParseError jpe;
    auto jdoc = QJsonDocument::fromJson(data, &jpe);
    auto const synonyms = jdoc.array();
    for (auto &value : synonyms) {
      auto object = value.toObject();
      auto s = object.value("<VALUE">).toString();
      if (!result.isEmpty())
        result.append(QLatin1String(", "))
      result.append(s);
    }
    reply->deleteLater();
    cont(result);
  });
}

懒惰的风格要求使用的代码getData是可重新启动的,并且也允许继续传递,只要继续连接到信号:

class Class : public QObject {
  Q_OBJECT
  QString m_cachedData;
  QNetworkAccessManager m_manager{this};
  Q_SIGNAL void dataAvailable(const QString &);
  ...
};

QString Class::getData(const QString &urlSuffix) {
  if (!m_cachedData.isEmpty())
    return m_cachedData;

  auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
  QNetworkRequest request(QUrl(urlString));
  auto *reply = m_manager.get(request);
  QObject::connect(reply, &QNetworkReply::finished, this, [=]{
    m_cachedData.clear();
    auto data = reply->readAll();
    QJsonParseError jpe;
    auto jdoc = QJsonDocument::fromJson(data, &jpe);
    auto const synonyms = jdoc.array();
    for (auto &value : synonyms) {
      auto object = value.toObject();
      auto s = object.value("<VALUE">).toString();
      if (!m_cachedData.isEmpty())
        m_cachedData.append(QLatin1String(", "))
      m_cachedData.append(s);
    }
    reply->deleteLater();
    emit dataAvailable(m_cachedData);
  });
  return {};
}

状态机将状态进程形式化:

class Class : public QObject {
  Q_OBJECT
  QStateMachine m_sm{this};
  QNetworkAccessManager m_manager{this};
  QPointer<QNetworkReply> m_reply;
  QState s_idle{&m_sm}, s_busy{&m_sm}, s_done{&m_sm};
  Q_SIGNAL void to_busy();
  void getData(const QString &);
  ...

};

Class::Class(QObject * parent) : QObject(parent) {
  m_sm.setInitialState(&s_idle);
  s_idle.addTransition(this, &Class::to_busy, &s_busy);
  s_done.addTransition(&s_idle);
  m_sm.start();
}

void Class::getData(const QString &urlSuffix) {
  static char const kInit[] = "initialized";
  auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
  QNetworkRequest request(QUrl(urlString));
  m_reply = m_manager.get(request);
  s_busy.addTransition(reply, &QNetworkReply::finished, &s_done);
  to_busy();
  if (!s_done.property(kInit).toBool()) {
    QObject::connect(&s_done, &QState::entered, this, [=]{
      QString result;
      auto data = m_reply->readAll();
      QJsonParseError jpe;
      auto jdoc = QJsonDocument::fromJson(data, &jpe);
      auto const synonyms = jdoc.array();
      for (auto &value : synonyms) {
        auto object = value.toObject();
        auto s = object.value("<VALUE">).toString();
        if (!result.isEmpty())
          result.append(QLatin1String(", "))
        result.append(s);
      }
      m_reply->deleteLater();
    });
    s_done.setProperty(kInit, true);
  }
}
于 2018-04-05T15:14:02.250 回答
1

当然这是错误的:stringToReturn在 中声明为局部变量getData,如果您使用它将是 const ,并且如果通过引用捕获,[=]它将在函数对象的调用时通过信号消失。finishedLambda 表达式中将有一个悬空引用。这是放置用作此函数对象输出的变量的坏地方,该变量在从getData.

此处的 lambda 表达式创建的函数对象在返回后被 getData调用。它会在由于发送请求而触发信号时被调用,它是一个异步处理程序在这种情况下,getData 不是 lambda 的调用者。lambda 的调用者是信号槽系统。如果 getData 没有显式调用 lambda,我们无法保证在本地存储的生命周期结束之前返回函数对象。

this如果可以保证this( instance) 在被触发<CLASS_NAME>时仍然“活动”,则此处可能的解决方法是使用 的字段。finished()本质上,这个“getData”无法返回该值,除非您暂停它直到请求完成(这会破坏异步方法)。

实际上,finished会在何时QNetworkReply::finished被解雇,QNetworkAccessManager::get 只是发布请求并立即返回所说的回复。接收数据时触发信号(发出readyRead)

于 2018-04-05T07:37:05.343 回答