“可变函数”的正确替代方案;在 c++

发布于 2024-10-04 04:12:18 字数 1606 浏览 7 评论 0原文

在 C++ 中将算法包装在类中时,我经常遇到 const 正确性的问题。我觉得我想要一个可变的函数,尽管这是不允许的。谁能告诉我如何实现下面的类?

以下是我想要编写的代码。

  • 函数 run() 不应该是 const 函数,因为它改变了 数据。
  • 函数 get_result() 应该 是一个常量函数(就用户而言),因为它返回 数据。

但是,如果用户请求结果而不调用 run(),我希望 get_result() 函数运行该算法。这破坏了 const 的正确性,因为我有一个 const 函数调用一个非常量函数。

class operate_on_data
{
  std::vector<double> m_data;  // the data to modify
  bool m_completed;  // check to see if the function run() has been called
public:
  operate_on_data(std::vector<double> data)
    : m_data(data), m_completed(false) {}  //initialise
  void run() //I don't want this function to be const  
  {
    //The algorithm goes here - it alters m_data.
    m_completed = true;  //the algorithm has been run
  }
  std::vector<double> get_result() const //I want this function to be const
  {
    /*The following breaks const correctness because 
      I am calling a non-const function from a const function
      - but this feels like the right thing to do ... */ 
    if (!m_completed) run();  //run the algorithm if it has not run already
    return m_data; //return
  }
};

我成功编译上述类的唯一方法是

  • 使 run() const 并使 m_data 和 m_completed 可变。这是可行的,但在概念上是错误的,因为 run() 明显会更改数据。
  • 使 get_result() 不是一个常量函数。这似乎也是错误的,因为用户希望这个函数是一个简单的返回,因此是恒定的。
  • 将 run() 函数放入 get_result() const 函数中,并使数据变量可变。

我的理解是 mutable 关键字是为第三个选项设计的,其中实现需要更改数据,但用户合理地期望一个简单的返回,因此 const 函数。

但是,我不想执行此最终选项,因为我希望用户能够准确选择何时更改数据。然而,他们有可能忘记调用 run(),因此如果他们请求结果而不调用 run(),我想强制该算法。我觉得想要使 run() 可变 - 但我不被允许这样做。

编写这样一个类的正确方法是什么?

I often have a problem with const correctness when wrapping algorithms in classes in c++. I feel that I want a mutable function, although this is not allowed. Can anyone advise me how to implement classes such as the one that follows?

The following is the code that I want to write.

  • The function run() should not be a
    const function because it changes the
    data.
  • The function get_result() should
    be a constant function (as far as the user is concerned) because it returns
    the data.

However, if the user requests the result without calling run(), I want the get_result() function to run the algorithm. This breaks the const correctness because I have a const function calling a non-const function.

class operate_on_data
{
  std::vector<double> m_data;  // the data to modify
  bool m_completed;  // check to see if the function run() has been called
public:
  operate_on_data(std::vector<double> data)
    : m_data(data), m_completed(false) {}  //initialise
  void run() //I don't want this function to be const  
  {
    //The algorithm goes here - it alters m_data.
    m_completed = true;  //the algorithm has been run
  }
  std::vector<double> get_result() const //I want this function to be const
  {
    /*The following breaks const correctness because 
      I am calling a non-const function from a const function
      - but this feels like the right thing to do ... */ 
    if (!m_completed) run();  //run the algorithm if it has not run already
    return m_data; //return
  }
};

The only way I have been I have managed to compile the above class is to either

  • make run() const, and make m_data and m_completed mutable. This works but is conceptually wrong because run() demonstrably changes data.
  • make get_result() not a constant function. This seems wrong too, for the user would expect this function to be a simple return, and therefore constant.
  • Put the run() function into the get_result() const function and make the data variables mutable.

My understanding was that the mutable keyword was designed for the third of these options, where the implementation requires data to be changed but the user reasonably expects a simple return and therefore const function.

However, I don't want to do this final option because I want the user to be able to choose exactly when they change the data. There is a chance that they will forget to call run(), however, and so I want to force the algorithm if they request the result without calling run(). I feel like a want to make run() mutable - but I'm not allowed to.

What is the correct way to write such a class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

辞取 2024-10-11 04:12:18

使 run() 常量,并使 m_data 和 m_completed 可变。这是可行的,但在概念上是错误的,因为 run() 明显改变了数据。

实际上并非如此。事实上,你的类中的变量已经改变,但你永远无法证明这一点。调用 run() 不会更改用户能够从类的接口中检索的任何内容。如果您无法检索有关此类更改的任何信息,那么您就不可能演示该更改。这不仅仅是一个语义问题,它直接说明了“mutable”关键字的全部要点。

“mutable”关键字被严重误解了。

也就是说,尽管根据我所掌握的最少信息,我可能会按照上述方式进行操作,但我并不推荐这样做。几乎肯定有一种更好的方法,从更广泛的角度来看待您的问题。

我可能使用的另一种方法显然是您要避免的:强制用户在使用 get_data() 之前调用 run() 。但说实话,这也是一个非常次优的方法。也许更是如此。

编辑:

如果您确实决定使用可变方法,那么我建议进行一些更改。拥有一个名为“run()”的 const 函数并且不返回任何有趣的内容会非常令人困惑。这个函数当然应该是非常量的。因此,如果已经决定这样做,我要做的是让 run() 调用一个 const 和私有函数,该函数具有当前 'run()' 函数的行为,该函数也由 get_data( )在规定的条件下。

make run() const, and make m_data and m_completed mutable. This works but is conceptually wrong because run() demonstrably changes data.

Not true, actually. The variables within your class are, in fact, altered but you could never, ever demonstrate this. Calling run() does not change anything that the user is able to retrieve from your class's interface. If you can't retrieve any information about such a change then you can't possibly demonstrate that change. This is more than a semantic issue, it speaks directly to the whole point of the 'mutable' keyword.

The 'mutable' keyword is greatly misunderstood.

That said, though with the minimally given information I have I might do it the above way, I'm not recommending it. There's almost certainly a better method that would be apparent given a larger view of your problem.

The other method I might use is what you're apparently set on avoiding: force the user to call run() before using get_data(). To tell the truth though, this is a really suboptimal method too. Perhaps more so.

Edit:

If you do decide to use the mutable method then I'd suggest some changes. Having a function called 'run()' that is const and returns nothing of interest would be quite confusing. This function should certainly be non-const. Thus, what I would do, given a decision to do it this way already, is have run() call a const and private function that has the behavior of the current 'run()' function, which is also referred to by get_data() under the specified conditions.

百合的盛世恋 2024-10-11 04:12:18

一些抽象的注释可能会帮助您澄清事情:

  • const 方法是那些不修改对象的概念“状态”的方法,
  • const 方法是那些修改对象 的概念“状态”的方法。 。
  • 此外,可变字段是每个对象的字段,但不被视为对象概念状态的一部分(就像一些延迟评估和记住的缓存值)。

问题可能是 operate_on_data 可能并不是真正定义良好的类。什么是“operate_on_data”类的对象?这个对象的“状态”是什么?什么不是?这听起来很尴尬(至少对我来说)——并且某些设计的听起来很尴尬的描述可能表明是反直觉的设计。

我的想法是,您将“操作”和“操作结果”的不同概念保留在一个奇怪的类中,这会导致混乱。

Some abstract remarks which may help you clarify things:

  • const methods are those which don't modify the conceptual "state" of an objects,
  • non-const method are those which do.
  • Additionally, mutable fields are those which are per-object, but not considered a part of the object's conceptual state (like some cached values which are evaluated lazily and remembered).

The problem might be that operate_on_data may not really be a well-defined class. What is an object of class "operate_on_data"? What is the "state" of this object? What is not? This sounds awkward (at least to me) - and awkward-sounding description of some design may indicate counter-intuitive design.

My thought is that you're keeping the different concepts of an "operation" and an "operation result" in one strange class, which leads to confusion.

一百个冬季 2024-10-11 04:12:18

我认为你的问题是语义问题,而不是语法问题。

在我看来,在不首先调用 run() 的情况下请求结果是一个错误,并且应该会导致异常。

如果它不是一个错误并且确实应该是可能的,那么我认为首先使用 run() 没有任何意义,所以只需放弃它并完成所有工作即可(非常量)get_result()

I think your problem is a semantic, not a syntactic one.

Requesting the result without calling run() first is an error, in my eyes, and should result in an exception.

If it is not an error and should indeed be possible, I see no sense in having run() in the first place, so just drop it and do all the work in the (non-const) get_result().

柏拉图鍀咏恒 2024-10-11 04:12:18

只需将其设置为错误(界面的一部分)即可在运行算法之前获取结果。然后,将工作与结果分开,使两者都能够正确地指示其常量。如果您尝试在运行算法之前调用 get 方法以向客户端表明他们做错了什么,则 get 方法可能会抛出异常。

Just make it an error (part of the interface) to get the result prior to running the algorithm. Then you separate the work from the results, allowing both to properly indicate their constness. The get method can throw if you attempt to call it prior to running the algorithm to indicate to the client they're doing something wrong.

风苍溪 2024-10-11 04:12:18

如果 get_result() 可能实际更改数据,那么它就不是 const。如果您希望它是 const,请不要调用 run(),而是抛出异常。

您应该使用 mutable 来缓存数据,即不会更改实例状态且仅出于性能原因而存储的数据。

If get_result() may actually change data, it is not const. If you want it to be const, don't call run() but rather throw an exception.

You should use mutable for cached data, i.e. things that do not change the state of your instance and are only stored for performance reasons.

半寸时光 2024-10-11 04:12:18

如果我处于这个位置,我可能会抛出一个例外。

但是,您可能逃脱

if (!m_completed) (const_cast <operate_on_data *> (this))->run();

但是,如果随后在实际上已定义为operate_on_data实例上调用get_result const,您输入 lala-land。

If I ever come into that position, I'd probably throw an exception.

However, you may get away with

if (!m_completed) (const_cast <operate_on_data *> (this))->run();

However, if get_result is then called on an instance of operate_on_data that has actually been defined to be const, you enter lala-land.

血之狂魔 2024-10-11 04:12:18

const的概念取决于类的实现;重要的是逻辑语义,而不是字段级别/函数级别的常量。因此,如果需要从 const 调用非常量方法,只需进行 const 强制转换即可。我更喜欢:

void X::foo(  ) const {
      X & self = const_cast<X &>(*this);
      self.bar( ); //"bar" non-const function
}

The concept of const is up to the implementation of the class; what's important is the logical semantics, not field level / function level constness. Therefore, if one needs to call a non-const method from const, simply const cast. I prefer:

void X::foo(  ) const {
      X & self = const_cast<X &>(*this);
      self.bar( ); //"bar" non-const function
}
黎歌 2024-10-11 04:12:18

如果 run() 在对象中唯一更改的是 m_completed,那么可以声明 m_completed mutablerun()< /代码> <代码>常量。如果 run() 更改了其他内容,那么调用 get_result() 也会更改这些其他内容,这意味着 get_result() 绝对不应该 <代码>常量。

不过,为了完善讨论,您会注意到每个 STL 容器都有两个 begin() 函数和两个 end() 函数。一个 begin() 和一个 end() 函数将返回可变迭代器,而另一个 begin()end()< /code> 函数将返回 const_iterator。

事实上,可以使用 const 和非 const 版本重载 run()。然后 get_result() 将调用 run()const 版本,因为它将被视为唯一合法的选项:

class operate_on_data
{
    std::vector<double> m_data;
    bool m_completed;
public:
    operate_on_data(std::vector<double> data)
        : m_data(data), m_completed(false) { }
    void run()
    {
       //The algorithm goes here - it alters m_data.
       m_completed = true;
    }

    void run() const
    {
        // something that does not modify m_data or m_completed
    }
    std::vector<double> get_result() const
    {
        if (!m_completed)
            run();
        return m_data;
    }
};

但是,这只有意义如果 run()const 版本不会更改任何状态。否则,run() 的非 const 性质将泄漏到 get_result() 中,并使 const- get_result() 是一个公然的谎言。


我认为示例代码有些人为。如果没有,您实际上是在采用以下方法:

std::vector<double> results = do_calculation(data);

并将其包装在具有非常薄的接口的对象中(即返回 std::vector< 的 get_results() 方法) /代码>)。我没有看到对象化版本有太多改进。如果您想缓存计算结果,通常只需将 std::vector 保留在将创建并使用此值的代码周围的 int 上,这通常是很有意义的目的。

If the only thing that run() changes in the object is m_completed then it's fine to declare m_completed mutable and run() const. If run() changes other things, then calling get_result() will also change those other things, meaning get_result() should definitely not be const.

However, to round out the discussion, you will notice that the STL containers each have two begin() functions and two end() functions. One begin() and one end() function will return mutable iterators, while the other begin() and end() functions will return const_iterators.

It is in fact possible to overload run() with a const and non-const version. Then get_result() will call the const version of run() because it will be considered the only legal option:

class operate_on_data
{
    std::vector<double> m_data;
    bool m_completed;
public:
    operate_on_data(std::vector<double> data)
        : m_data(data), m_completed(false) { }
    void run()
    {
       //The algorithm goes here - it alters m_data.
       m_completed = true;
    }

    void run() const
    {
        // something that does not modify m_data or m_completed
    }
    std::vector<double> get_result() const
    {
        if (!m_completed)
            run();
        return m_data;
    }
};

However, that only makes sense if the const version of run() doesn't change any state. Otherwise the non-const-ness of run() will leak into get_result() and make the const-ness of get_result() a blatant lie.


I assume that the example code is somewhat contrived. If not, you're essentially taking this:

std::vector<double> results = do_calculation(data);

And wrapping it in an object with a very thin interface (namely, a get_results() method that returns the std::vector<double>). I don't see much of an improvement in the object-ized version. If you want to cache the results of your calculation, it usually makes a lot of sense to do that by simply keeping the std::vector<double> around int the code that would have created and used this object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文