我担心我违反了 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/