如何定义触发计算的常量吸盘?
How to define const getters that trigger a calculation?
我有一个类,其中包含一个偶尔需要重新计算的数据成员,尽管在需要重新计算之前可能会多次访问它。 我已经为数据定义了常量和非常量 getter 方法,在 getter 方法中,我想检查是否需要运行计算,如果需要,请调用适当的方法。 在非 const getter 中,我检查该值是否是最新的,如果没有,则在返回数据之前运行计算。在 const getter 中执行相同的操作会导致编译器抱怨此对象是 const 类型,而计算方法是非常量类型。因此,如果我简单地在常量 getter 中调用非 const getter,代码就会编译,我感到很惊讶。有人可以解释一下为什么这有效,因为我有点困惑。
class A {
bool isUpToDate=false;
double someData=0;
// Do some calculation
void calcData()
{
someData = doSomeCalc();
isUpToDate = true; // data is now up-to-date
}
// non-const getter
double& data()
{
if(!isUpToDate)
{
// run the calculation only if data is not up-to-date
calcData()
}
return someData;
}
// const getter that doesn't work
const double& data() const
{
if(!isUpToDate)
{
calcData() // compiler error: "this object is type const A but
calcData is non-const"
}
return someData;
}
// const getter that (mysteriously) works
const double& data() const
{
return data(); // why doesn't the compiler complain that data() is non-const?
}
我确信这种行为实际上是合理且定义明确的,我只是好奇它为什么有效,因为我不明白这里发生了什么。我正在使用 g++ 作为我的编译器和 c++11 标准,以防这是为什么它工作的相关因素。
你不是在称呼非常量getter;你是在称呼自己:
const double& data() const
{
return data();
}
是无限递归。
由于data() const
,*this
在方法中被有效地const
。调用data()
等效于this->data()
。因为this
是一个const A *
,这会再次选择const
getter。
因此,我很惊讶代码编译
当然它可以编译,但它实际上不起作用。它要么进入无限循环并挂起,要么只是崩溃。
在这种情况下,您可以将someData
和isUpToDate
声明为mutable
。这意味着甚至可以从const
成员函数修改它们。然后使calcData()
const
,以便可以从另一个const
成员函数调用它。
这种做法往往会被怀疑!我将其用于互斥体和缓存。我担心你的可能有点过分了。
你真的需要data()
才能const
吗?你应该确保...
使该字段可变,并使用更新函数:
bool updateNeeded;
double getNewValue();
class MyResource {
mutable double someData = 0.0;
void update() const {
// Because someData is mutable, this works fine
if(updateNeeded()) {
someData = getNewValue();
}
}
public:
double& getData() {
update();
return someData;
}
double const& getData() const {
update();
return someData;
}
};
当你有一个像const double& data() const
这样的成员函数时,data
调用的所有函数也必须是 const 的,并且不能修改对象。对于那些偶然发现这一点的人:"const"在类的函数声明中最后的含义?
有一些方法可以解决这个问题:
-
您可以将
void calcData()
更改为void calcData() const
这将告诉编译器计算方法是常量。这在这里不起作用,因为您正在分配结果,因此它是非常量(它修改成员变量),因此编译器会对您大喊大叫。 -
您可以将
double const& getData() const
更改为double const& getData()
。但是,如果您有时需要A
,这将不起作用。 -
另一个解决方案是
class A { bool isUpToDate=false; double someData=0; double& data() { if(!isUpToDate) { // run the calculation only if data is not up-to-date someData = doSomeCalc(); isUpToDate = true; } return someData; } double data() const // Don't change anything, just return by value { if(!isUpToDate) { return doSomeCalc(); } return someData; } }
这将按值返回并重新计算所有内容。缺点是,如果doSomeCalc()
密集,则性能可能会很差,因为它每次都需要调用它并且无法更新someData
。
- 其他答案已经提到使用
mutable
限定符来表示someData(和isUpToDate)。这将正常工作,但有些人可能反对使用可变关键字,因为它们通常用于特定目的,如果处理不当可能会很危险。
- 类似枚举的计算常量
- 如何计算常量字符**中的总字符数?
- 从另一个静态常量数组初始化静态常量数组(只需少量计算)
- 如何在常量计算表达式中获取编译时错误?
- 强制在编译时计算类的类的常量成员
- 如何定义触发计算的常量吸盘?
- C++ 计算编译时常量,同时防止整数常量溢出
- 当短路禁用常量表达式的计算时,是否允许在常量表达式中读取"一过一"指针
- 将计算结果保存到常量参考中
- 常量操作是否在运行时计算
- 如何计算初始化一个常量数组(制作常量查找表)
- 编译器计算常量表达式
- C++ 运行时计算的常量变量
- 将constexpr(用于在编译时计算常量)替换为模板
- 在 for 循环条件下计算常量
- C++如何正确计算常量字符中的字符数
- 表达式在C++VS中未计算为常量
- 奇怪的错误 C2131:表达式在 VC 2015 中未计算为常量
- 在智能感知中从枚举中计算字符串常量
- 在编译时计算常量库函数