Recently C23 added two new attributes: unsequenced and reproducible, which are now supported by GCC alongside the existing pure and const.
I am slightly confused by the exact differences between all of them, and all of the sources I could find only compared one or two of them, not all four at once.
Is the following understanding I have of them correct?
| Attribute | Side Effects | Output | Read Globals [0] | Write Globals [0] | Read Pointer Args | Write Pointer Args | Examples | Optimisations |
|---|---|---|---|---|---|---|---|---|
| Pure | no | return | yes [1] | no | yes | no | strlen, memcpy |
Elide calls where globals and args have not changed |
| Const | no | return | const [2] | no | const [2] | no | abs |
All calls with the same arguments have the same value; evaluate at compile-time |
| Unsequenced | yes | return | const | no | yes | no | strlen |
Reorder calls |
| Reproducible | yes | return or args | const | no | yes | yes | memcpy |
Elide repeated calls with the same arguments |
| None | yes | return or args | yes | yes | yes | yes | printf |
None |
Sources
- What are the [[reproducible]] and [[unsequenced]] attributes in C23, and when should I use them?
- What is the difference between C++11's constexpr and C23's [[reproducible]]?
- __attribute__((const)) vs __attribute__((pure)) in GNU C
- https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
Footnotes
[0]:
By "globals" I include static and thread_local variables
[1]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
functions declared with the pure attribute can safely read any non-volatile objects
[2]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute
This seems to be allowed, but recommended against:
functions declared with the [const] attribute can safely read objects that do not change their return value, such as non-volatile constants
Note that a function that has pointer arguments and examines the data pointed to must not be declared const if the pointed-to data might change between successive invocations of the function
In general, since a function cannot distinguish data that might change from data that cannot, const functions should never take pointer or, in C++, refrence arguments