pure/const function attributes in different compilers

Solution 1:

  • GCC: pure/const function attributes
  • llvm-gcc: supports the GCC pure/const attributes
  • Clang: seems to support it (I tried on a simple example with the GCC style attributes and it worked.)
  • ICC: seems to adopt the GCC attributes (Sorry, only a forum post.)
  • MSVC: Seems not to support it. (discussion)

In general, it seems that almost all compilers support the GCC attributes. MSVC is so far the only compiler which does not support them (and which also doesn't have any alternative).

Solution 2:

First, it's useful to note that "const" is a more strict version of "pure", so "pure" can be used as a fallback if a compiler doesn't implement "const".

As others have mentioned, MSVC doesn't really have anything similar, but a lot of compilers have adopted the GCC syntax, including many which don't define __GNUC__ (and some which sometimes do and sometimes don't, depending on flags).

  • GCC supports pure since 2.96+, and const since 2.5.0, in case you feel like checking the version.
  • Clang supports both; you can use __has_attribute(pure) and __has_attribute(const) to detect them, but it's probably fine to just rely on clang setting __GNUC__. This also includes compilers based on clang like emscripten and XL C/C++ 13+.
  • Intel C/C++ Compiler supports both, but their documentation is terrible so I have no idea when they were added. 16.0+ is certainly safe.
  • Oracle Developer Studio 12.2+ supports both.
  • ARM C/C++ Compiler 4.1+ (and possibly older) supports both pure and const
  • IBM XL C/C++ since at least 10.1.
  • TI 8.0+
  • TI 7.3+ with --gcc (detected with __TI_GNU_ATTRIBUTE_SUPPORT__) supports both.
  • PGI doesn't document it (AFAICT), but both attributes work (or are at least silently ignored). 17.10+ is safe, though they've probably been acceptable for much longer.

Of these, clang always defines __GNUC__ and friends (currently to 4.2, IIRC). Intel defines __GNUC__ by default (though it can be suppressed with -no-gcc) as does PGI in C++ mode (but not in C mode). The others you'll have to check for manually.

Oracle Developer Studio has also supported pragmas since it was known as Forte Developer 6. They're used a bit differently since they require you to specify the function name:

/* pure: */
#pragma does_not_write_global_data (funcname)
/* const; SPARC-only until 12.2 */
#pragma no_side_effect (funcname)

TI 6.0+ (at least) supports a #pragma FUNC_IS_PURE; pragma in C++ mode only. In C mode, it's #pragma FUNC_IS_PURE(funcname);.

Most of this can be hidden behind a macro, which is what I've done in Hedley:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(pure,2,96,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_PURE __attribute__((__pure__))
#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
#  define HEDLEY_NO_RETURN _Pragma("FUNC_IS_PURE;")
#else
#  define HEDLEY_PURE
#endif

#if HEDLEY_GNUC_HAS_ATTRIBUTE(const, 2, 5, 0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_CONST __attribute__((__const__))
#else
#  define HEDLEY_CONST HEDLEY_PURE
#endif

This doesn't include the variants which would require the function names as an argument, but it still covers the vast majority of users, and it's safe to use everywhere.

If you don't want to use Hedley (it's a single public domain / CC0 header) it shouldn't be too difficult to replace the internal version macros. If you choose to do that, you should probably base your port on the Hedley repo instead of this answer as I'm much more likely to keep it up to date.

Solution 3:

As an update for anyone reading this after all these years, starting with C++11 (and with continuous improvements with C++14, C++17 and C++20), the constexpr keyword was added to the language, letting you mark functions, methods, statements, templates, variables (pretty much everything) as absolutely and completely constant, allowing for nice optimization from the compiler.

constevel and constinit were also added in C++20 to further expand the compile-time capabilities of the language.