The GCC C++ compiler offers a family of extensions via function attributes, such as:
int square(int) __attribute__((const));
Two attributes in particular, const and pure, allow you to declare that a function’s evaluation has no side effects and depends only on its arguments (const), or only on its arguments and global variables (pure). This allows for common subexpression elimination, which may have the effect that such a function gets called fewer times than it is written in the code.
My question is whether this can be used safely, correctly and sensibly for virtual member functions:
struct Foo
{
virtual int square(int) __attribute__((pure)); // does that make sense?
};
Does this have any sensible semantics? Is it allowed at all? Or is it just ignored? I’m afraid I can’t find the answer to this in the GCC documentation.
The reason for this question is that there is a family of compiler options -Wsuggest-attribute which make GCC produce suggestions of where those attributes might be placed to improve the code. However, it seems to end up making these suggestions even for virtual functions, and I wonder whether those suggestions should be taken seriously.
The first question is whether these attributes even have valid semantics for virtual methods. In my opinion they do. I would expect that if a virtual function was labelled pure you would be promising the compiler that all implementations only rely on their arguments and the data in global memory (and do not change that), where data in global memory would also include the contents of the object. If a virtual function were labelled const this would mean it could depend on only on its arguments, it would not even be allowed to inspect the contents of the object. The compiler would have to enforce that all overriding virtual methods declare attributes at least as strong as their parents.
The next question is whether GCC uses these attributes to make optimisations. In the following test program you can see that version 4.6.3 does not (try compiling to assembler with -O3 and you will see the loop is unrolled).
Even in the following program where the type is known at compile time the compiler does not optimize the loop.
Removing the inheritance from A (and thus making the method non-virtual) allows the compiler to make the expected optimisation.
There is no standard or documentation regarding these attributes, so the best reference we can have is the implementation. As they currently have no effect I would suggest avoiding their use on virtual methods in case the behaviour changes unexpectedly in the future.