c++ - 在 const 方法中将 'mutable' 用于异步填充缓存

标签 c++ qt c++11

我担心我违反了 mutable 的约定,我使用它在异步执行按需请求的数据模型中缓存信息。数据模型恰好是 Qt,尽管这不是特别重要的事实。

class MyDataModel : public QAbstractItemModel
{
public:
    QVariant data( const QModelIndex & index, int role ) const override;

private:
    void SignalRowDataUpdated( int row ) const;
    mutable SimpleRowCache mCache;
};

data() 被调用时,我检查缓存看是否有它。如果没有,我会立即返回空数据(以避免阻塞 UI)并向 API 发送异步请求以填充缓存。由于 data() 必须是常量,这就要求 mCache 是可变的。 data() 的内部结构如下所示:

RowData row_data = mCache.Get( row );
if( !row_data )
{
    // Store empty data in cache, to avoid repeated API requests
    mCache.Set( row, RowData() );

    // Invoke API with a lambda to deliver async result.  Note: 'this' is const
    auto data_callback = [this, row]( RowData data )
    {
        mCache.Set( row, std::move(data) );
        SignalRowDataUpdated( row );
    };
    DataApi::GetRowData( row, data_callback );

    return QVariant::Invalid;
}
return row_data[ column ];

我担心的是数据模型对象的逻辑常量在这里被违反了:为某些索引调用data() 会直接导致 future 使用相同参数的调用返回不同的值。

这是个坏主意吗?是否存在“正确”执行此操作的通用模式/范例?


脚注:我对 SignalRowDataUpdated() 也有类似的问题。这实际上是一个围绕发出 Qt 信号的包装器:emit dataChanged( from, to ),这是一个非常量调用。我通过在构造时在 lambda 中捕获 this 来处理这个问题,允许我从 const 函数调用非 const 方法。我不为此感到骄傲 =(

最佳答案

来自 C++ 语言标准中的 [class.this] 部分,

If the member function is declared const, the type of this is const X*

所以 const 改变了 this 的类型;关于一个类的“逻辑常量”没有什么(我发现的)。由于这是指向 MyDataModel 的常量指针,您可以用它做什么? [decl.type.cv] 告诉我们

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

data() 中对 mCache 进行更改是允许的,因为它被声明为 mutable

没有要求 const 成员函数在使用相同参数调用时必须返回相同的值,因为内部状态可以通过更新可变成员或对非 const 成员函数的干预调用来改变。

总而言之,只要您的调用代码可以处理三个不同的返回值(QVariant::Invalid,如果当前正在加载数据,则为空的RowData 对象, 和一个加载的行)你不应该对成员函数声明有任何问题。

当您将新加载的数据移动到缓存中时,您可能在回调中遇到问题。如果另一个线程试图同时加载该行的数据,它可以获得一个混合了空行和已加载行的 RowData 对象。

SignalRowDataUpdated 中,为什么要通过 lambda 进行间接访问,而不是在 this 上执行 const_cast

关于c++ - 在 const 方法中将 'mutable' 用于异步填充缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40796662/

相关文章:

c++ - 右值引用和 std::move

c++ - 我们可以使用函数指针创建适配器模式吗?

css - QSpinBox 箭头放置在行编辑之外(css)

c++ - 如何将输入焦点设置到 Qt 中显示的对话框?

c++ - 模板方法和默认模板参数

c++ - 检查二维数组中是否存在任何数字的程序

c++ - 如何告诉 std::priority_queue 刷新其排序?

c++ - 我可以更改 QT 中主应用程序的图标大小吗?

c++ - 将 C++ 头文件转换为 Python

c++ - Qt 和音频播放