GCC `__attribute__ ((pure))` 关于 "input state" getter 方法的建议 - 正确吗?

GCC `__attribute__ ((pure))` suggestion on "input state" getter method - correct?

使用 -Wsuggest-attribute=pure 编译使 GCC 建议可以使用 __attribute__ ((pure)) 标记的潜在函数以进行优化。

这是 GCC 文档中的definition of pure

Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure.

我正在创建一个小型游戏引擎,其中有一个 input_context class,其中包含一个 input_state 成员。 input_context class 通过从操作系统获取全局输入状态来每帧更新 input_state 成员。

里面还有几个"getters"查询输入状态

简化示例:

class input_context
{
private:
    input_state _input_state;

public:
    void update()
    {
        os::fill_input_state(_input_state);
    }

    auto mouse_x() const noexcept
    { 
        return _input_state._mouse_x;
    }

    auto mouse_y() const noexcept
    { 
        return _input_state._mouse_y;
    }

    auto is_key_down(keycode k) const noexcept
    { 
        // `_keys` is an array of `bool` values.
        return _input_state._keys[k];
    }
};

GCC 告诉我所有这些 "getter methods",如 mouse_x()mouse_y()is_key_down(),都是 __attribute__ ((pure)) 的候选者。

我应该将这些方法标记为 pure 吗?

我不这么认为,但是 GCC 的建议让我想知道。

我不确定如何解释 GCC 对 pure 的定义 - 它说 仅依赖参数 and/or 全局变量 的函数应该被标记为这样。

我认为您可以将这些方法标记为纯方法,因为它们没有副作用。

根据您引用的文档,纯函数可以依赖于全局 variable/external 状态。我猜这也和闭包的概念有关。您可以根据外部变量定义函数 f(例如使用 Haskell):

x=1
f y = x + y

函数本身仍然没有副作用(尽管在 Haskell 中,您不能更改 x 的值,而您可以在 [=30] 之外更改 x =] 在 C/C++).

pureconst的区别总结在这个问题中:

引用 :

attribute((const)) is the same as attribute((pure)) but without any access to global variables.

我认为将其标记为纯净是可以的。 以简化形式考虑您的示例,并添加了一些 IO 函数:

#include <stdio.h>
class X {
  int x_=0;
public:
  int x() const noexcept __attribute__ ((pure)) /*__attribute__((noinline))*/;
  void inc() noxcept { x_++; }
};
int X::x() const noexcept { puts("getting x"); return x_;}
int main(){
  X x;
  printf("%d\n", x.x() + x.x() + x.x());
  x.inc();
  printf("%d\n", x.x() + x.x() + x.x());
}

让你得到:

getting x
0
getting x
3

而不是

getting x
getting x
getting x
0
getting x
getting x
getting x
3

在优化级别至少 -O1(在更高级别,您可能需要添加 __attribute__((noinline)) 以防止内联)。

如果在对这些 getter 的两次连续调用之间状态发生变化,只要编译器可以检测到状态已发生变化,这没有关系。 如果您需要 运行 一个 non-const 方法来改变状态,那么这不违反纯度。但是,如果状态更改 on its own(系统更改 it/another 线程更改 it/a 信号处理程序更改它),编译器无法知道更改,那么 pure 属性不再合法。