Reputation: 303017
The gcc documentation for __attribute__((pure))
states:
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
.
What does it mean to only depend on the parameters? Consider:
struct Wrapper {
int i;
int get() const { return i; }
void set(int x) { i = x; }
};
Is it valid to label Wrapper::get()
as a pure
member function? It only depends on the implicit Wrapper
instance, but that data could change.
Upvotes: 18
Views: 1041
Reputation: 32484
Is it valid to label
Wrapper::get()
as apure
member function? It only depends on the implicitWrapper
instance, but that data could change.
Yes, Wrapper::get()
meets the requirements of gcc's pure
attribute. Note, however, that __attribute__((pure))
doesn't mean pure in the academic sense, i.e. possessing the property of referential transparency. The latter can be communicated through a stricter __attribute__((const))
:
__attribute__((const))
Many functions do not examine any values except their arguments, and have no effects except the return value. Basically this is just slightly more strict class than the
pure
attribute below, since function is not allowed to read global memory.Note that a function that has pointer arguments and examines the data pointed to must not be declared
const
. Likewise, a function that calls a non-const
function usually must not beconst
. It does not make sense for aconst
function to returnvoid
.
But since Wrapper::get()
doesn't possess the property of referential transparency implied by __attribute__((const))
, it cannot be labeled as such.
EDIT
The guarantee about the pure
-ness (in gcc's sense) of a function can be used for optimizing only a block of code that doesn't contain writes to global memory (and, in particular, is not interspersed by calls to non-pure
functions). Examples:
struct Wrapper {
int i;
int get() const __attribute__((pure)) { return i; }
void set(int x) { i = x; }
};
long foo(Wrapper* w)
{
// w->get() can be computed once
return 2 * w->get() * w->get();
}
long bar(Wrapper* w)
{
// w->get() can be computed once (even though below code writes to memory,
// that memory is freshly allocated and cannot be accessed by w->get())
long result = 2;
result *= w->get();
result *= w->get();
return result;
}
long baz(Wrapper* w)
{
// both w->get()s must be evaluated, since non-pure code occurs between them
long result = 2;
result *= w->get();
std::cout << "Result after the first muliplication: " << result << std::endl;
result *= w->get();
return result;
}
Upvotes: 6