Compiler Hints

static  (C89) / (C++98)

  • Static has two fundamental effects, depending on where it appears:

    • Changes storage duration (object lives for the entire program).

    • Changes linkage (name is visible only within the translation unit).

  • Global variable :

    • Internal linkage + static storage duration

  • Local variable :

    • Static storage duration

  • Global function :

    • Simply makes the function private to the Translation Unit.

    • The linker does not export its symbol.

    • Other translation units cannot call it, even if they declare it with extern .

    • If a function is only used inside tht TU, making it static expresses that intent.

    • Because the compiler knows nobody outside the Translation Unit can call the function, it can sometimes:

      • inline it more aggressively

      • remove it entirely if unused

      • specialize it

      • rename or merge it freely.

    • In C :

      • It's the traditional way to give a function internal linkage.

    • In C++ :

      • It's preferred to use Anonymous Namespaces for the same result.

  • Class data member (C++) :

    • One variable shared by all instances.

  • Class member function (C++) :

    • No this pointer; callable without an object.

    • Makes the function now belong to the class, not to the instance.

export  (C++98->C++20)

  • C++98:

    • Exported templates

    • The feature failed, as it turned out to be extremely difficult to implement.

    • A compiler had to:

      • track template definitions across translation units,

      • instantiate templates later,

      • manage complex dependency information.

    • Almost no compiler implemented it.

  • C++11:

    • The original export  keyword remained reserved, but the exported-template feature was effectively dead.

  • C++20:

    • Completely repurposed export .

    • Now it exports names from a module.

    • It means "This declaration is visible outside the module. Non-exported declarations remain internal"

    export module math;
    
    export int add(int a, int b) {
        return a + b;
    }
    
    • In another file:

    import math;
    
    int x = add(3, 4);
    
  • export  vs extern :

    • extern

      • Declares that a symbol is defined elsewhere (linkage).

    • export  (C++20)

      • Makes a declaration part of a module's public interface.

explicit  (C++98)

  • Its purpose is to prevent the compiler from performing implicit conversions through constructors or conversion operators.

  • C++98 — introduced explicit  for constructors.

    • Without explicit :

      struct String {
          String(const char* s) {}
      };
      
      void print(String s);
      
      int main() {
          print("hello"); // OK
      }
      
    • With explicit :

      struct String {
          explicit String(const char* s) {}
      };
      
      void print(String s);
      
      int main() {
          print("hello");          // Error
          print(String("hello"));  // OK
      }
      
      struct Vec2 {
          explicit Vec2(float v)
              : x(v), y(v) {}
      
          float x, y;
      };
      
      Vec2 a(5.0f);      // OK
      Vec2 b = 5.0f;     // Error
      
      • Direct initialization is allowed, but copy initialization is not, as that would require an implicit conversion.

  • C++11 — extended it to conversion operators:

    explicit operator bool() const;
    
    • Without explicit :

      struct File {
          operator bool() const {
              return true;
          }
      };
      
      File f;
      
      if (f) { }      // OK
      bool b = f;     // OK
      int x = f;      // Also possible through bool -> int
      
    • With explicit :

      struct File {
          explicit operator bool() const {
              return true;
          }
      };
      
      File f;
      
      if (f) { }                 // OK (special case)
      bool b = f;                // Error
      bool b2 = static_cast<bool>(f); // OK
      
  • C++20 — introduced conditional explicit :

    template<typename T>
    explicit(sizeof(T) > 4)
    MyType(T);
    
    • The constructor is explicit only if the constant expression evaluates to true.

[[assume(expression)]]  (C++23)

  • Gives optimizer a condition assumed to be true.

  • Undefined behavior if false at runtime.

[[assume(x > 0)]];
  • Low-level optimization hint.

  • Vendor equivalents (both languages):

    • __builtin_assume

    • __assume  (MSVC)

  • Used to eliminate bounds checks and unreachable branches.

_Static_assert  (C11) / static_assert  (C++11)

Compile-time assertion (not attribute).

_Static_assert(sizeof(int) == 4, "Unexpected size");

constexpr  (C++11)

  • Allows evaluation at compile time.

  • The compiler may compute the result during compilation.

  • Used for:

    • lookup tables

    • compile-time constants

    • metaprogramming

constexpr int square(int x) {
    return x * x;
}
int a = square(4);

const  (C89, C++98)

  • const  is not the same guarantee as constexpr  in C++. The key difference is:

    • const  → read-only (may or may not be compile-time)

    • constexpr  → must be compile-time

const int MAX = 64;
  • You cannot modify  MAX

  • But it is not guaranteed to be a compile-time constant in all contexts

consteval  (C++20)

  • .

constinit  (C++20)

  • .

_Noreturn  (C11), [[noreturn]]  (C++11)

  • Indicates a function never returns.

[[noreturn]] void fatal();
  • They are semantic guarantees, not just documentation. They can affect code generation.

  • They state that a function:

    • never returns normally

    • either calls exit, abort, longjmp, or loops forever

    • reaching the end is undefined behavior

  • The compiler:

    • does not require a return

    • removes code after the call

    • may remove stack cleanup logic

restrict  (C99)

  • For the lifetime of the pointer the object it points to is accessed only through that pointer (or a value directly derived from it).

  • Enables aliasing optimizations.

  • Tells compiler pointers do not alias.

  • Very significant in hot loops:

    • enables vectorization

    • enables load/store reordering

    • reduces reloads from memory

    • improves auto-SIMD generation

  • Without restrict, compiler must conservatively assume aliasing.

  • Incorrect use causes undefined behavior and real miscompilations.

void f(int * restrict p);
  • C++ has no standard equivalent (vendors support __restrict).

[[no_unique_address]]  (C++20)

  • Allows a non-static data member (typically empty type) to occupy no storage.

struct Empty {};

struct S {
    int x;
    [[no_unique_address]] Empty e;
};
  • Without it:

    • e  may consume 1 byte

  • With it:

    • e  can overlap with padding

    • object size may shrink

  • Optimization impact

    • reduces object size

    • improves cache density

    • improves container packing

[[reproducible]]  (C23)

  • Guarantees that the function:

    • has no observable side effects

    • depends only on its arguments and possibly global state

    • produces consistent results

[[reproducible]]
int pure_compute(int x);
  • Vendor equivalents in both languages:

    • __attribute__((pure))

  • Optimization impact

    • Allows:

      • common subexpression elimination

      • redundant call elimination

      • hoisting out of loops

      • reordering relative to unrelated side effects

  • The compiler may transform:

    int a = f(10);
    int b = f(10);
    
  • into:

    int a = f(10);
    int b = a;
    

[[unsequenced]]  (C23)

  • Stronger than [[reproducible]].

  • Indicates:

    • no side effects

    • no synchronization

    • no inter-thread interaction

    • can be evaluated in unsequenced context

[[unsequenced]]
int fast_math(int x);
  • Vendor equivalents in both languages:

    • __attribute__((const))

  • Optimization impact

    • Allows:

      • aggressive reordering

      • call merging

      • vectorization

      • evaluation in arbitrary order

      • removal if result unused