Barry
Barry

Reputation: 303017

Annotating a const member function with pure attribute

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

Answers (1)

Leon
Leon

Reputation: 32484

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.

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 be const. It does not make sense for a const function to return void.

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

Related Questions