使用 -Wsuggest-attribute=pure
编译使 GCC 建议可以使用 __attribute__ ((pure))
标记的潜在函数以进行优化。
以下是 GCC 文档中pure
的定义:
我许多函数除了返回值之外没有任何影响,它们的返回值仅取决于参数和/或全局变量。这样的函数可以像算术运算符一样进行公共子表达式消除和循环优化。这些函数应使用属性 pure 声明。
正在创建一个小型游戏引擎,其中我有一个包含input_state
成员的input_context
类。input_context
类通过从操作系统获取全局输入状态来更新每帧input_state
成员。
它还包含几个用于查询输入状态的"getter"。
简化示例:
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方法",如mouse_x()
,mouse_y()
和is_key_down()
,都是__attribute__ ((pure))
的候选者。
我应该将这些方法标记为pure
吗?
我不这么认为,但GCC的建议让我对此感到疑惑。
我不确定如何解释 GCC 对pure
的定义 - 它说仅依赖参数和/或全局变量的函数应该被标记为这样。
在某种程度上,全局操作系统输入状态可以解释为全局变量。
另一方面,"getter 方法"总是返回不同的值,具体取决于
_input_state
成员变量。
标记为纯是可以的。以简化的形式考虑您的示例,并添加了一些 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("%dn", x.x() + x.x() + x.x());
x.inc();
printf("%dn", x.x() + x.x() + x.x());
}
Pure允许您获得:
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
更改(系统更改它/另一个线程更改它/信号处理程序更改它(,则 pure
属性不再是合法的。
您可以将这些方法标记为纯方法,因为它们没有副作用。
根据您引用的文档,纯函数可以依赖于全局变量/外部状态。我想这也与关闭的概念有关。您可以根据外部变量定义函数f
(例如使用 Haskell(:
x=1
f y = x + y
函数本身仍然没有副作用(尽管在 Haskell 中,你不能更改 x
的值,而你可以在 C/C++ 中更改函数/方法之外的x
(。
pure
和 const
之间的区别总结在这个问题中: GNU C 中的 __attribute__((const(( 与 __attribute__((pure((
引用那里的答案:
attribute((const(( 与 attribute((pure(( 相同,但无法访问全局变量。