Аннотирование функции const-члена с чистым атрибутом

Документация gcc для __attribute__((pure)) гласит:

Многие функции не имеют эффектов, кроме возвращаемого значения, а их возвращаемое значение зависит только от параметров и/или глобальных переменных. Такая функция может быть подвержена общему исключению подвыражения и оптимизации цикла, как и в случае с арифметическим оператором. Эти функции должны быть объявлены с атрибутом pure.

Что значит только зависеть от параметров? Рассмотрим:

struct Wrapper {
    int i;

    int get() const { return i; }
    void set(int x) { i = x; }
};

Является ли оно допустимым для метки Wrapper::get() как функции-члена pure? Это зависит только от неявного экземпляра Wrapper, но эти данные могут измениться.

Ответы

Ответ 1

Является ли оно допустимым для метки Wrapper::get() как функции члена pure? Это зависит только от неявного экземпляра Wrapper, но эти данные могут измениться.

Да, Wrapper::get() соответствует требованиям атрибута gcc pure. Обратите внимание, однако, что __attribute__((pure)) не означает чистое в академическом смысле, то есть обладает свойством ссылочной прозрачности. Последнее может быть передано через более строгий __attribute__((const)):

__attribute__((const))

Многие функции не проверяют значения, кроме их аргументов, и не имеют эффектов, кроме возвращаемого значения. В основном это просто чуть более строгий класс, чем атрибут pure ниже, поскольку функция не может читать глобальную память.

Обратите внимание, что функция, имеющая аргументы указателя и анализирующая данные указываемый не должен быть объявлен const. Аналогично, функция, которая вызывает функция не const обычно не должна быть const. Это не имеет смысла для функции const для возврата void.

Но так как Wrapper::get() не обладает свойством ссылочной прозрачности, подразумеваемым __attribute__((const)), он не может быть помечен как таковой.

ИЗМЕНИТЬ

Гарантия на pure -ness (в смысле gcc) функции может использоваться для оптимизации только блока кода, который не содержит записи в глобальную память (и, в частности, не перемежается вызовами к функциям не pure). Примеры:

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;
}