如果公共状态不变,是否可以接受`const_cast<Foo*>(this)`?

问题描述 投票:0回答:2

我有一个C++类,可以在运行中计算一些统计参数。是否可以接受将const从 this 来实现冗长计算的缓存,如果我不改变公开可见的状态,而 const 的函数是幂等的,而且是纯粹透明的?

#include <cstdio>
#include <unistd.h>

class Compute {
public:
    Compute() = default;
    ~Compute() = default;

    void add(int x) {
        sum += x;
        squareDirty = true;
    }

    int getSquare() const {
        if (squareDirty) {
            auto &mthis = *const_cast<Compute*>(this);
            usleep(2000); // takes a long time!
            mthis.squareCached = sum * sum;
            mthis.squareDirty = false;
        }

        return squareCached;
    }

private:
    int sum = 0;
    bool squareDirty = false;
    int squareCached;
};

void foo() {
    Compute c{};
    c.add(10);
    c.add(20);
    printf("%d\n", c.getSquare()); // long time...
    printf("%d\n", c.getSquare()); // fast!
}

我想只在真正需要的时候才懒得计算东西--并缓存它们直到新的数据到来。

然而,我希望在实际需要的时候才懒得计算这些东西--并将它们缓存起来,直到新的数据到来。缓存 事情的意思是,我 T getSquare() const 方法将不得不抛开 constthis 来突变私人国家。

但由于 getSquare 是有效的idempotent,编译器可以计算一次,然后存储为常量,或者内联,或者做其他任何事情,因为我的私有状态是可以丢弃的。

这是一个可以接受的事情,还是我自找的UB?

c++ const c++20
2个回答
1
投票

这个问题可以通过使用 mutable 关键字。这个关键字表示成员变量可以在 const 的限定函数。

struct S
{
  mutable int x;  // some data that is not relevant to users of the object (e.g. a cache variable)
  int y;

  void f() const 
  {
     x = 42;  // fine 
     y = 42;  // error
  }
};

这是在const限定函数中修改变量的更好的方法, 比起放弃const-correctness, 或者用 const_cast (这是UB的一个潜在来源)。

请注意,当你做一个函数 const你实际上保证了比 "可见状态不被改变 "更多的东西。你保证通过任何 const 函数对对象的访问是线程安全的。这意味着,如果你有 mutable 变量,如果多个线程可能同时访问它们,你应该仔细考虑它们的状态。在这种情况下,你可能应该考虑自己手动同步对这个变量的访问。


3
投票

是否可以接受 const_cast<Foo*>(this) 如果公共状态没有改变?

修改const对象的非可变状态是未定义的行为。

出于这个原因,在const cast之后通过引用const来修改非可变状态是不可接受的,除非可以证明引用的对象是非const的。

该状态是否为公共状态是不相关的。我想,这种类型的透明缓存是可变成员存在的原因。


1
投票

使用

auto &mthis = *const_cast<Compute*>(this);
mthis.squareCached = sum * sum;
mthis.squareDirty = false;

可以导致未定义的行为。这取决于原始对象是如何构造的。

https:/en.cppreference.comwcpplanguageconst_cast。:

修改a const 对象通过非const 访问路径,并指向 volatile 对象通过非volatile glvalue导致未定义的行为。

最好是将相关成员 mutable.

int sum = 0;
mutable bool squareDirty = false;
mutable int squareCached;

然后,你可以使用。

int getSquare() const {
    if (squareDirty) {
        usleep(2000); // takes a long time!
        this->squareCached = sum * sum;
        this->squareDirty = false;
    }

    return squareCached;
}

而不必担心未定义的行为。

© www.soinside.com 2019 - 2024. All rights reserved.